add support for complex dependencies
[platform/upstream/libsolv.git] / src / rules.c
1 /*
2  * Copyright (c) 2007-2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * rules.c
10  *
11  * SAT based dependency solver
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <assert.h>
19
20 #include "solver.h"
21 #include "solver_private.h"
22 #include "bitmap.h"
23 #include "pool.h"
24 #include "poolarch.h"
25 #include "util.h"
26 #include "evr.h"
27 #include "policy.h"
28 #include "solverdebug.h"
29 #include "linkedpkg.h"
30 #include "cplxdeps.h"
31
32 #define RULES_BLOCK 63
33
34 static void addrpmruleinfo(Solver *solv, Id p, Id d, int type, Id dep);
35 static void solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded);
36
37 /*-------------------------------------------------------------------
38  * Check if dependency is possible
39  *
40  * mirrors solver_dep_fulfilled but uses map m instead of the decisionmap.
41  * used in solver_addrpmrulesforweak and solver_createcleandepsmap.
42  */
43
44 static inline int
45 dep_possible(Solver *solv, Id dep, Map *m)
46 {
47   Pool *pool = solv->pool;
48   Id p, pp;
49
50   if (ISRELDEP(dep))
51     {
52       Reldep *rd = GETRELDEP(pool, dep);
53       if (rd->flags >= 8)
54          {
55           if (rd->flags == REL_AND || rd->flags == REL_COND)
56             {
57               if (!dep_possible(solv, rd->name, m))
58                 return 0;
59               return dep_possible(solv, rd->evr, m);
60             }
61           if (rd->flags == REL_OR)
62             {
63               if (dep_possible(solv, rd->name, m))
64                 return 1;
65               return dep_possible(solv, rd->evr, m);
66             }
67           if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
68             return solver_splitprovides(solv, rd->evr);
69           if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_INSTALLED)
70             return solver_dep_installed(solv, rd->evr);
71         }
72     }
73   FOR_PROVIDES(p, pp, dep)
74     {
75       if (MAPTST(m, p))
76         return 1;
77     }
78   return 0;
79 }
80
81 static inline int
82 is_otherproviders_dep(Pool *pool, Id dep)
83 {
84   if (ISRELDEP(dep))
85     {
86       Reldep *rd = GETRELDEP(pool, dep);
87       if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
88         return 1;
89     }
90   return 0;
91 }
92
93 /********************************************************************
94  *
95  * Rule handling
96  *
97  * - unify rules, remove duplicates
98  */
99
100 /*-------------------------------------------------------------------
101  *
102  * compare rules for unification sort
103  *
104  */
105
106 static int
107 unifyrules_sortcmp(const void *ap, const void *bp, void *dp)
108 {
109   Pool *pool = dp;
110   Rule *a = (Rule *)ap;
111   Rule *b = (Rule *)bp;
112   Id *ad, *bd;
113   int x;
114
115   x = a->p - b->p;
116   if (x)
117     return x;                          /* p differs */
118
119   /* identical p */
120   if (a->d == 0 && b->d == 0)
121     return a->w2 - b->w2;              /* assertion: return w2 diff */
122
123   if (a->d == 0)                       /* a is assertion, b not */
124     {
125       x = a->w2 - pool->whatprovidesdata[b->d];
126       return x ? x : -1;
127     }
128
129   if (b->d == 0)                       /* b is assertion, a not */
130     {
131       x = pool->whatprovidesdata[a->d] - b->w2;
132       return x ? x : 1;
133     }
134
135   /* compare whatprovidesdata */
136   ad = pool->whatprovidesdata + a->d;
137   bd = pool->whatprovidesdata + b->d;
138   while (*bd)
139     if ((x = *ad++ - *bd++) != 0)
140       return x;
141   return *ad;
142 }
143
144 int
145 solver_rulecmp(Solver *solv, Rule *r1, Rule *r2)
146 {
147   return unifyrules_sortcmp(r1, r2, solv->pool);
148 }
149
150
151 /*-------------------------------------------------------------------
152  *
153  * unify rules
154  * go over all rules and remove duplicates
155  */
156
157 void
158 solver_unifyrules(Solver *solv)
159 {
160   Pool *pool = solv->pool;
161   int i, j;
162   Rule *ir, *jr;
163
164   if (solv->nrules <= 2)               /* nothing to unify */
165     return;
166
167   /* sort rules first */
168   solv_sort(solv->rules + 1, solv->nrules - 1, sizeof(Rule), unifyrules_sortcmp, solv->pool);
169
170   /* prune rules
171    * i = unpruned
172    * j = pruned
173    */
174   jr = 0;
175   for (i = j = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
176     {
177       if (jr && !unifyrules_sortcmp(ir, jr, pool))
178         continue;                      /* prune! */
179       jr = solv->rules + j++;          /* keep! */
180       if (ir != jr)
181         *jr = *ir;
182     }
183
184   /* reduced count from nrules to j rules */
185   POOL_DEBUG(SOLV_DEBUG_STATS, "pruned rules from %d to %d\n", solv->nrules, j);
186
187   /* adapt rule buffer */
188   solv->nrules = j;
189   solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
190
191   /*
192    * debug: log rule statistics
193    */
194   IF_POOLDEBUG (SOLV_DEBUG_STATS)
195     {
196       int binr = 0;
197       int lits = 0;
198       Id *dp;
199       Rule *r;
200
201       for (i = 1; i < solv->nrules; i++)
202         {
203           r = solv->rules + i;
204           if (r->d == 0)
205             binr++;
206           else
207             {
208               dp = solv->pool->whatprovidesdata + r->d;
209               while (*dp++)
210                 lits++;
211             }
212         }
213       POOL_DEBUG(SOLV_DEBUG_STATS, "  binary: %d\n", binr);
214       POOL_DEBUG(SOLV_DEBUG_STATS, "  normal: %d, %d literals\n", solv->nrules - 1 - binr, lits);
215     }
216 }
217
218 #if 0
219
220 /*
221  * hash rule
222  */
223
224 static Hashval
225 hashrule(Solver *solv, Id p, Id d, int n)
226 {
227   unsigned int x = (unsigned int)p;
228   int *dp;
229
230   if (n <= 1)
231     return (x * 37) ^ (unsigned int)d;
232   dp = solv->pool->whatprovidesdata + d;
233   while (*dp)
234     x = (x * 37) ^ (unsigned int)*dp++;
235   return x;
236 }
237 #endif
238
239
240 /*-------------------------------------------------------------------
241  *
242  */
243
244 /*
245  * add rule
246  *  p = direct literal; always < 0 for installed rpm rules
247  *  d, if < 0 direct literal, if > 0 offset into whatprovides, if == 0 rule is assertion (look at p only)
248  *
249  *
250  * A requires b, b provided by B1,B2,B3 => (-A|B1|B2|B3)
251  *
252  * p < 0 : pkg id of A
253  * d > 0 : Offset in whatprovidesdata (list of providers of b)
254  *
255  * A conflicts b, b provided by B1,B2,B3 => (-A|-B1), (-A|-B2), (-A|-B3)
256  * p < 0 : pkg id of A
257  * d < 0 : Id of solvable (e.g. B1)
258  *
259  * d == 0: unary rule, assertion => (A) or (-A)
260  *
261  *   Install:    p > 0, d = 0   (A)             user requested install
262  *   Remove:     p < 0, d = 0   (-A)            user requested remove (also: uninstallable)
263  *   Requires:   p < 0, d > 0   (-A|B1|B2|...)  d: <list of providers for requirement of p>
264  *   Updates:    p > 0, d > 0   (A|B1|B2|...)   d: <list of updates for solvable p>
265  *   Conflicts:  p < 0, d < 0   (-A|-B)         either p (conflict issuer) or d (conflict provider) (binary rule)
266  *                                              also used for obsoletes
267  *   ?:          p > 0, d < 0   (A|-B)
268  *   No-op ?:    p = 0, d = 0   (null)          (used as policy rule placeholder)
269  *
270  *   resulting watches:
271  *   ------------------
272  *   Direct assertion (no watch needed) --> d = 0, w1 = p, w2 = 0
273  *   Binary rule: p = first literal, d = 0, w2 = second literal, w1 = p
274  *   every other : w1 = p, w2 = whatprovidesdata[d];
275  *   Disabled rule: w1 = 0
276  *
277  *   always returns a rule for non-rpm rules
278  */
279
280 Rule *
281 solver_addrule(Solver *solv, Id p, Id d)
282 {
283   Pool *pool = solv->pool;
284   Rule *r = 0;
285   Id *dp = 0;
286
287   int n = 0;                           /* number of literals in rule - 1
288                                           0 = direct assertion (single literal)
289                                           1 = binary rule
290                                           >1 =
291                                         */
292
293   /* it often happenes that requires lead to adding the same rpm rule
294    * multiple times, so we prune those duplicates right away to make
295    * the work for unifyrules a bit easier */
296
297   if (!solv->rpmrules_end)              /* we add rpm rules */
298     {
299       r = solv->rules + solv->nrules - 1;       /* get the last added rule */
300       if (r->p == p && r->d == d && (d != 0 || !r->w2))
301         return r;
302     }
303
304     /*
305      * compute number of literals (n) in rule
306      */
307
308   if (d < 0)
309     {
310       /* always a binary rule */
311       if (p == d)
312         return 0;                      /* ignore self conflict */
313       n = 1;
314     }
315   else if (d > 0)
316     {
317       for (dp = pool->whatprovidesdata + d; *dp; dp++, n++)
318         if (*dp == -p)
319           return 0;                     /* rule is self-fulfilling */
320         
321       if (n == 1)                       /* convert to binary rule */
322         d = dp[-1];
323     }
324
325   if (n == 1 && p > d && !solv->rpmrules_end)
326     {
327       /* smallest literal first so we can find dups */
328       n = p; p = d; d = n;             /* p <-> d */
329       n = 1;                           /* re-set n, was used as temp var */
330     }
331
332   /*
333    * check for duplicate
334    */
335
336   /* check if the last added rule (r) is exactly the same as what we're looking for. */
337   if (r && n == 1 && !r->d && r->p == p && r->w2 == d)
338     return r;  /* binary rule */
339
340     /* have n-ary rule with same first literal, check other literals */
341   if (r && n > 1 && r->d && r->p == p)
342     {
343       /* Rule where d is an offset in whatprovidesdata */
344       Id *dp2;
345       if (d == r->d)
346         return r;
347       dp2 = pool->whatprovidesdata + r->d;
348       for (dp = pool->whatprovidesdata + d; *dp; dp++, dp2++)
349         if (*dp != *dp2)
350           break;
351       if (*dp == *dp2)
352         return r;
353    }
354
355   /*
356    * allocate new rule
357    */
358
359   /* extend rule buffer */
360   solv->rules = solv_extend(solv->rules, solv->nrules, 1, sizeof(Rule), RULES_BLOCK);
361   r = solv->rules + solv->nrules++;    /* point to rule space */
362
363     /*
364      * r = new rule
365      */
366
367   r->p = p;
368   if (n == 0)
369     {
370       /* direct assertion, no watch needed */
371       r->d = 0;
372       r->w1 = p;
373       r->w2 = 0;
374     }
375   else if (n == 1)
376     {
377       /* binary rule */
378       r->d = 0;
379       r->w1 = p;
380       r->w2 = d;
381     }
382   else
383     {
384       r->d = d;
385       r->w1 = p;
386       r->w2 = pool->whatprovidesdata[d];
387     }
388   r->n1 = 0;
389   r->n2 = 0;
390
391   IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
392     {
393       POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "  Add rule: ");
394       solver_printrule(solv, SOLV_DEBUG_RULE_CREATION, r);
395     }
396
397   return r;
398 }
399
400 void
401 solver_shrinkrules(Solver *solv, int nrules)
402 {
403   solv->nrules = nrules;
404   solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
405 }
406
407 /******************************************************************************
408  ***
409  *** rpm rule part: create rules representing the package dependencies
410  ***
411  ***/
412
413 /*
414  *  special multiversion patch conflict handling:
415  *  a patch conflict is also satisfied if some other
416  *  version with the same name/arch that doesn't conflict
417  *  gets installed. The generated rule is thus:
418  *  -patch|-cpack|opack1|opack2|...
419  */
420 static Id
421 makemultiversionconflict(Solver *solv, Id n, Id con)
422 {
423   Pool *pool = solv->pool;
424   Solvable *s, *sn;
425   Queue q;
426   Id p, pp, qbuf[64];
427
428   sn = pool->solvables + n;
429   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
430   queue_push(&q, -n);
431   FOR_PROVIDES(p, pp, sn->name)
432     {
433       s = pool->solvables + p;
434       if (s->name != sn->name || s->arch != sn->arch)
435         continue;
436       if (!MAPTST(&solv->multiversion, p))
437         continue;
438       if (pool_match_nevr(pool, pool->solvables + p, con))
439         continue;
440       /* here we have a multiversion solvable that doesn't conflict */
441       /* thus we're not in conflict if it is installed */
442       queue_push(&q, p);
443     }
444   if (q.count == 1)
445     return -n;  /* no other package found, generate normal conflict */
446   return pool_queuetowhatprovides(pool, &q);
447 }
448
449 static inline void
450 addrpmrule(Solver *solv, Id p, Id d, int type, Id dep)
451 {
452   if (!solv->ruleinfoq)
453     solver_addrule(solv, p, d);
454   else
455     addrpmruleinfo(solv, p, d, type, dep);
456 }
457
458 #ifdef ENABLE_LINKED_PKGS
459
460 static void
461 addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m, Queue *workq)
462 {
463   Pool *pool = solv->pool;
464   int i;
465   if (!qr->count)
466     return;
467 #if 0
468   printf("ADDLINKS %s\n -> %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
469   for (i = 0; i < qr->count; i++)
470     printf("    - %s\n", pool_solvid2str(pool, qr->elements[i]));
471   printf(" <- %s\n", pool_dep2str(pool, prv));
472   for (i = 0; i < qp->count; i++)
473     printf("    - %s\n", pool_solvid2str(pool, qp->elements[i]));
474 #endif
475
476   if (qr->count == 1)
477     addrpmrule(solv, qr->elements[0], -(s - pool->solvables), SOLVER_RULE_RPM_PACKAGE_REQUIRES, req);
478   else
479     addrpmrule(solv, -(s - pool->solvables), pool_queuetowhatprovides(pool, qr), SOLVER_RULE_RPM_PACKAGE_REQUIRES, req);
480   if (qp->count > 1)
481     {
482       Id d = pool_queuetowhatprovides(pool, qp);
483       for (i = 0; i < qr->count; i++)
484         addrpmrule(solv, -qr->elements[i], d, SOLVER_RULE_RPM_PACKAGE_REQUIRES, prv);
485     }
486   else if (qp->count)
487     {
488       for (i = 0; i < qr->count; i++)
489         addrpmrule(solv, qp->elements[0], -qr->elements[i], SOLVER_RULE_RPM_PACKAGE_REQUIRES, prv);
490     }
491   if (!m)
492     return;     /* nothing more to do if called from getrpmruleinfos() */
493   for (i = 0; i < qr->count; i++)
494     if (!MAPTST(m, qr->elements[i]))
495       queue_push(workq, qr->elements[i]);
496   for (i = 0; i < qp->count; i++)
497     if (!MAPTST(m, qp->elements[i]))
498       queue_push(workq, qp->elements[i]);
499   if (solv->installed && s->repo == solv->installed)
500     {
501       Repo *installed = solv->installed;
502       /* record installed buddies */
503       if (!solv->instbuddy)
504         solv->instbuddy = solv_calloc(installed->end - installed->start, sizeof(Id));
505       if (qr->count == 1)
506         solv->instbuddy[s - pool->solvables - installed->start] = qr->elements[0];
507       for (i = 0; i < qp->count; i++)
508         {
509           Id p = qp->elements[i];
510           if (pool->solvables[p].repo != installed)
511             continue;   /* huh? */
512           if (qp->count > 1 || (solv->instbuddy[p - installed->start] != 0 && solv->instbuddy[p - installed->start] != s - pool->solvables))
513             solv->instbuddy[p - installed->start] = 1;  /* 1: ambiguous buddy */
514           else
515             solv->instbuddy[p - installed->start] = s - pool->solvables;
516         }
517     }
518 }
519
520 static void
521 add_package_link(Solver *solv, Solvable *s, Map *m, Queue *workq)
522 {
523   Queue qr, qp;
524   Id req = 0, prv = 0;
525   queue_init(&qr);
526   queue_init(&qp);
527   find_package_link(solv->pool, s, &req, &qr, &prv, &qp);
528   if (qr.count)
529     addlinks(solv, s, req, &qr, prv, &qp, m, workq);
530   queue_free(&qr);
531   queue_free(&qp);
532 }
533
534 #endif
535
536 #ifdef ENABLE_COMPLEX_DEPS
537
538 static void
539 add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *workq, Map *m)
540 {
541   Pool *pool = solv->pool;
542   Repo *installed = solv->installed;
543   int i, j;
544   Queue bq;
545
546   queue_init(&bq);
547
548   /* CNF expansion for requires, DNF + INVERT expansion for conflicts */
549   i = pool_normalize_complex_dep(pool, dep, &bq, type == SOLVER_RULE_RPM_PACKAGE_REQUIRES ? 0 : (CPLXDEPS_TODNF | CPLXDEPS_EXPAND | CPLXDEPS_INVERT));
550   /* handle special cases */
551   if (i == 0)
552     {
553       if (dontfix)
554         {
555           POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken dependency %s of installed package %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, p));
556         }
557       else
558         {
559           POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, p), p, pool_dep2str(pool, dep));
560           addrpmrule(solv, -p, 0, type == SOLVER_RULE_RPM_PACKAGE_REQUIRES ? SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP : type, dep);
561         }
562       queue_free(&bq);
563       return;
564     }
565   if (i == 1)
566     {
567       queue_free(&bq);
568       return;
569     }
570
571   /* go through all blocks and add a rule for each block */
572   for (i = 0; i < bq.count; i++)
573     {
574       if (!bq.elements[i])
575         continue;       /* huh? */
576       if (bq.elements[i] == pool->nsolvables)
577         {
578           /* conventional requires (cannot be a conflicts as they have been expanded) */
579           Id *dp = pool->whatprovidesdata + bq.elements[i + 1];
580           i += 2;
581           if (dontfix)
582             {
583               for (j = 0; dp[j] != 0; j++)
584                 if (pool->solvables[dp[j]].repo == installed)
585                   break;                /* provider was installed */
586               if (!dp[j])
587                 {
588                   POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken requires %s of installed package %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, p));
589                   continue;
590                 }
591             }
592           if (!*dp)
593             {
594               /* nothing provides req! */
595               POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, p), p, pool_dep2str(pool, dep));
596               addrpmrule(solv, -p, 0, SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP, dep);
597               continue;
598             }
599           addrpmrule(solv, -p, dp - pool->whatprovidesdata, SOLVER_RULE_RPM_PACKAGE_REQUIRES, dep);
600           /* push all non-visited providers on the work queue */
601           if (m)
602             for (; *dp; dp++)
603               if (!MAPTST(m, *dp))
604                 queue_push(workq, *dp);
605           continue;
606         }
607       if (!bq.elements[i + 1])
608         {
609           Id p2 = bq.elements[i];
610           /* simple rule with just two literals */
611           if (dontfix && p2 < 0 && pool->solvables[-p2].repo == installed)
612             continue;
613           if (dontfix && p2 > 0 && pool->solvables[p2].repo != installed)
614             continue;
615           if (p == p2)
616             continue;
617           if (-p == p2)
618             {
619               if (type == SOLVER_RULE_RPM_PACKAGE_CONFLICT)
620                 {
621                   if (pool->forbidselfconflicts && !is_otherproviders_dep(pool, dep))
622                     addrpmrule(solv, -p, 0, SOLVER_RULE_RPM_SELF_CONFLICT, dep);
623                   continue;
624                 }
625               addrpmrule(solv, -p, 0, type, dep);
626               continue;
627             }
628           if (p2 > 0)
629             addrpmrule(solv, p2, -p, type, dep);        /* hack so that we don't need pool_queuetowhatprovides */
630           else
631             addrpmrule(solv, -p, p2, type, dep);
632           if (m && p2 > 0 && !MAPTST(m, p2))
633             queue_push(workq, p2);
634         }
635       else
636         {
637           Id *qele;
638           int qcnt;
639
640           qele = bq.elements + i;
641           qcnt = i;
642           while (bq.elements[i])
643              i++;
644           qcnt = i - qcnt;
645           if (dontfix)
646             {
647               for (j = 0; j < qcnt; j++)
648                 {
649                   if (qele[j] > 0 && pool->solvables[qele[j]].repo == installed)
650                     break;
651                   if (qele[j] < 0 && pool->solvables[-qele[j]].repo != installed)
652                     break;
653                 }
654               if (j == qcnt)
655                 continue;
656             }
657           /* add -p to (ordered) rule (overwriting the trailing zero) */
658           for (j = 0; ; j++)
659             {
660               if (j == qcnt || qele[j] > -p)
661                 {
662                   if (j < qcnt)
663                     memmove(qele + j + 1, qele + j, (qcnt - j) * sizeof(Id));
664                   qele[j] = -p;
665                   qcnt++;
666                   break;
667                 }
668               if (qele[j] == -p)
669                 break;
670             }
671           /* check if the rule contains both p and -p */
672           for (j = 0; j < qcnt; j++)
673             if (qele[j] == p)
674               break;
675           if (j == qcnt)
676             {
677               /* hack: create fake queue 'q' so that we can call pool_queuetowhatprovides */
678               Queue q;
679               memset(&q, 0, sizeof(q));
680               q.count = qcnt - 1;
681               q.elements = qele + 1;
682               addrpmrule(solv, qele[0], pool_queuetowhatprovides(pool, &q), type, dep);
683               if (m)
684                 for (j = 0; j < qcnt; j++)
685                   if (qele[j] > 0 && !MAPTST(m, qele[j]))
686                     queue_push(workq, qele[j]);
687             }
688         }
689     }
690   queue_free(&bq);
691 }
692
693 #endif
694
695 /*-------------------------------------------------------------------
696  *
697  * add (install) rules for solvable
698  *
699  * s: Solvable for which to add rules
700  * m: m[s] = 1 for solvables which have rules, prevent rule duplication
701  *
702  * Algorithm: 'visit all nodes of a graph'. The graph nodes are
703  *  solvables, the edges their dependencies.
704  *  Starting from an installed solvable, this will create all rules
705  *  representing the graph created by the solvables dependencies.
706  *
707  * for unfulfilled requirements, conflicts, obsoletes,....
708  * add a negative assertion for solvables that are not installable
709  *
710  * It will also create rules for all solvables referenced by 's'
711  *  i.e. descend to all providers of requirements of 's'
712  *
713  */
714
715 void
716 solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
717 {
718   Pool *pool = solv->pool;
719   Repo *installed = solv->installed;
720
721   Queue workq;  /* list of solvables we still have to work on */
722   Id workqbuf[64];
723
724   int i;
725   int dontfix;          /* ignore dependency errors for installed solvables */
726   Id req, *reqp;
727   Id con, *conp;
728   Id obs, *obsp;
729   Id rec, *recp;
730   Id sug, *sugp;
731   Id p, pp;             /* whatprovides loops */
732   Id *dp;               /* ptr to 'whatprovides' */
733   Id n;                 /* Id for current solvable 's' */
734
735   queue_init_buffer(&workq, workqbuf, sizeof(workqbuf)/sizeof(*workqbuf));
736   queue_push(&workq, s - pool->solvables);      /* push solvable Id to work queue */
737
738   /* loop until there's no more work left */
739   while (workq.count)
740     {
741       /*
742        * n: Id of solvable
743        * s: Pointer to solvable
744        */
745
746       n = queue_shift(&workq);          /* 'pop' next solvable to work on from queue */
747       if (m)
748         {
749           if (MAPTST(m, n))             /* continue if already visited */
750             continue;
751           MAPSET(m, n);                 /* mark as visited */
752         }
753
754       s = pool->solvables + n;
755
756       dontfix = 0;
757       if (installed                     /* Installed system available */
758           && s->repo == installed       /* solvable is installed */
759           && !solv->fixmap_all          /* NOT repair errors in rpm dependency graph */
760           && !(solv->fixmap.size && MAPTST(&solv->fixmap, n - installed->start)))
761         {
762           dontfix = 1;                  /* dont care about broken rpm deps */
763         }
764
765       if (!dontfix)
766         {
767           if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC
768                 ? pool_disabled_solvable(pool, s)
769                 : !pool_installable(pool, s))
770             {
771               POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable\n", pool_solvid2str(pool, n), n);
772               addrpmrule(solv, -n, 0, SOLVER_RULE_RPM_NOT_INSTALLABLE, 0);
773             }
774         }
775
776 #ifdef ENABLE_LINKED_PKGS
777       /* add pseudo-package <-> real-package links */
778       if (has_package_link(pool, s))
779         add_package_link(solv, s, m, &workq);
780 #endif
781
782       /*-----------------------------------------
783        * check requires of s
784        */
785
786       if (s->requires)
787         {
788           reqp = s->repo->idarraydata + s->requires;
789           while ((req = *reqp++) != 0)            /* go through all requires */
790             {
791               if (req == SOLVABLE_PREREQMARKER)   /* skip the marker */
792                 continue;
793
794 #ifdef ENABLE_COMPLEX_DEPS
795               if (pool_is_complex_dep(pool, req))
796                 {
797                   /* we have AND/COND deps, normalize */
798                   add_complex_deprules(solv, n, req, SOLVER_RULE_RPM_PACKAGE_REQUIRES, dontfix, &workq, m);
799                   continue;
800                 }
801 #endif
802
803               /* find list of solvables providing 'req' */
804               dp = pool_whatprovides_ptr(pool, req);
805
806               if (*dp == SYSTEMSOLVABLE)          /* always installed */
807                 continue;
808
809               if (dontfix)
810                 {
811                   /* the strategy here is to not insist on dependencies
812                    * that are already broken. so if we find one provider
813                    * that was already installed, we know that the
814                    * dependency was not broken before so we enforce it */
815                   for (i = 0; (p = dp[i]) != 0; i++)
816                     if (pool->solvables[p].repo == installed)
817                       break;            /* found installed provider */
818                   if (!p)
819                     {
820                       /* didn't find an installed provider: previously broken dependency */
821                       POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken requires %s of installed package %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s));
822                       continue;
823                     }
824                 }
825
826               if (!*dp)
827                 {
828                   POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, n), n, pool_dep2str(pool, req));
829                   addrpmrule(solv, -n, 0, SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP, req);
830                   continue;
831                 }
832
833               IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
834                 {
835                   POOL_DEBUG(SOLV_DEBUG_RULE_CREATION,"  %s requires %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
836                   for (i = 0; dp[i]; i++)
837                     POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "   provided by %s\n", pool_solvid2str(pool, dp[i]));
838                 }
839
840               /* add 'requires' dependency */
841               /* rule: (-requestor|provider1|provider2|...|providerN) */
842               addrpmrule(solv, -n, dp - pool->whatprovidesdata, SOLVER_RULE_RPM_PACKAGE_REQUIRES, req);
843
844               /* push all non-visited providers on the work queue */
845               if (m)
846                 for (; *dp; dp++)
847                   if (!MAPTST(m, *dp))
848                     queue_push(&workq, *dp);
849             }
850         }
851
852       /* that's all we check for src packages */
853       if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
854         continue;
855
856       /*-----------------------------------------
857        * check conflicts of s
858        */
859
860       if (s->conflicts)
861         {
862           int ispatch = 0;
863
864           /* we treat conflicts in patches a bit differen:
865            * - nevr matching
866            * - multiversion handling
867            * XXX: we should really handle this different, looking
868            * at the name is a bad hack
869            */
870           if (!strncmp("patch:", pool_id2str(pool, s->name), 6))
871             ispatch = 1;
872           conp = s->repo->idarraydata + s->conflicts;
873           /* foreach conflicts of 's' */
874           while ((con = *conp++) != 0)
875             {
876 #ifdef ENABLE_COMPLEX_DEPS
877               if (!ispatch && pool_is_complex_dep(pool, con))
878                 {
879                   /* we have AND/COND deps, normalize */
880                   add_complex_deprules(solv, n, con, SOLVER_RULE_RPM_PACKAGE_CONFLICT, dontfix, &workq, m);
881                   continue;
882                 }
883 #endif
884               /* foreach providers of a conflict of 's' */
885               FOR_PROVIDES(p, pp, con)
886                 {
887                   if (ispatch && !pool_match_nevr(pool, pool->solvables + p, con))
888                     continue;
889                   /* dontfix: dont care about conflicts with already installed packs */
890                   if (dontfix && pool->solvables[p].repo == installed)
891                     continue;
892                   if (p == n)           /* p == n: self conflict */
893                     {
894                       if (!pool->forbidselfconflicts || is_otherproviders_dep(pool, con))
895                         continue;
896                       addrpmrule(solv, -n, 0, SOLVER_RULE_RPM_SELF_CONFLICT, con);
897                       continue;
898                     }
899                   if (ispatch && solv->multiversion.size && MAPTST(&solv->multiversion, p) && ISRELDEP(con))
900                     {
901                       /* our patch conflicts with a multiversion package */
902                       p = -makemultiversionconflict(solv, p, con);
903                     }
904                   /* rule: -n|-p: either solvable _or_ provider of conflict */
905                   addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_PACKAGE_CONFLICT, con);
906                 }
907             }
908         }
909
910       /*-----------------------------------------
911        * check obsoletes and implicit obsoletes of a package
912        * if ignoreinstalledsobsoletes is not set, we're also checking
913        * obsoletes of installed packages (like newer rpm versions)
914        */
915       if ((!installed || s->repo != installed) || !pool->noinstalledobsoletes)
916         {
917           int multi = solv->multiversion.size && MAPTST(&solv->multiversion, n);
918           int isinstalled = (installed && s->repo == installed);
919           if (s->obsoletes && (!multi || solv->keepexplicitobsoletes))
920             {
921               obsp = s->repo->idarraydata + s->obsoletes;
922               /* foreach obsoletes */
923               while ((obs = *obsp++) != 0)
924                 {
925                   /* foreach provider of an obsoletes of 's' */
926                   FOR_PROVIDES(p, pp, obs)
927                     {
928                       Solvable *ps = pool->solvables + p;
929                       if (p == n)
930                         continue;
931                       if (isinstalled && dontfix && ps->repo == installed)
932                         continue;       /* don't repair installed/installed problems */
933                       if (!pool->obsoleteusesprovides /* obsoletes are matched names, not provides */
934                           && !pool_match_nevr(pool, ps, obs))
935                         continue;
936                       if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
937                         continue;
938                       if (!isinstalled)
939                         addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_PACKAGE_OBSOLETES, obs);
940                       else
941                         addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_INSTALLEDPKG_OBSOLETES, obs);
942                     }
943                 }
944             }
945           /* check implicit obsoletes
946            * for installed packages we only need to check installed/installed problems (and
947            * only when dontfix is not set), as the others are picked up when looking at the
948            * uninstalled package.
949            */
950           if (!isinstalled || !dontfix)
951             {
952               FOR_PROVIDES(p, pp, s->name)
953                 {
954                   Solvable *ps = pool->solvables + p;
955                   if (p == n)
956                     continue;
957                   if (isinstalled && ps->repo != installed)
958                     continue;
959                   /* we still obsolete packages with same nevra, like rpm does */
960                   /* (actually, rpm mixes those packages. yuck...) */
961                   if (multi && (s->name != ps->name || s->evr != ps->evr || s->arch != ps->arch))
962                     continue;
963                   if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
964                     continue;
965                   if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
966                     continue;
967                   if (s->name == ps->name)
968                     addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_SAME_NAME, 0);
969                   else
970                     addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_IMPLICIT_OBSOLETES, s->name);
971                 }
972             }
973         }
974
975       if (m && pool->implicitobsoleteusescolors && (s->arch > pool->lastarch || pool->id2arch[s->arch] != 1))
976         {
977           int a = pool->id2arch[s->arch];
978           /* check lock-step candidates */
979           FOR_PROVIDES(p, pp, s->name)
980             {
981               Solvable *ps = pool->solvables + p;
982               if (s->name != ps->name || s->evr != ps->evr || MAPTST(m, p))
983                 continue;
984               if (ps->arch > pool->lastarch || pool->id2arch[ps->arch] == 1 || pool->id2arch[ps->arch] >= a)
985                 continue;
986               queue_push(&workq, p);
987             }
988         }
989
990       /*-----------------------------------------
991        * add recommends to the work queue
992        */
993       if (s->recommends && m)
994         {
995           recp = s->repo->idarraydata + s->recommends;
996           while ((rec = *recp++) != 0)
997             {
998               FOR_PROVIDES(p, pp, rec)
999                 if (!MAPTST(m, p))
1000                   queue_push(&workq, p);
1001             }
1002         }
1003       if (s->suggests && m)
1004         {
1005           sugp = s->repo->idarraydata + s->suggests;
1006           while ((sug = *sugp++) != 0)
1007             {
1008               FOR_PROVIDES(p, pp, sug)
1009                 if (!MAPTST(m, p))
1010                   queue_push(&workq, p);
1011             }
1012         }
1013     }
1014   queue_free(&workq);
1015 }
1016
1017 #ifdef ENABLE_LINKED_PKGS
1018 void
1019 solver_addrpmrulesforlinked(Solver *solv, Map *m)
1020 {
1021   Pool *pool = solv->pool;
1022   Solvable *s;
1023   int i, j;
1024   Queue qr;
1025
1026   queue_init(&qr);
1027   for (i = 1; i < pool->nsolvables; i++)
1028     {
1029       if (MAPTST(m, i))
1030         continue;
1031       s = pool->solvables + i;
1032       if (!s->repo || s->repo == solv->installed)
1033         continue;
1034       if (!strchr(pool_id2str(pool, s->name), ':'))
1035         continue;
1036       if (!pool_installable(pool, s))
1037         continue;
1038       find_package_link(pool, s, 0, &qr, 0, 0);
1039       if (qr.count)
1040         {
1041           for (j = 0; j < qr.count; j++)
1042             if (MAPTST(m, qr.elements[j]))
1043               {
1044                 solver_addrpmrulesforsolvable(solv, s, m);
1045                 break;
1046               }
1047           queue_empty(&qr);
1048         }
1049     }
1050   queue_free(&qr);
1051 }
1052 #endif
1053
1054 /*-------------------------------------------------------------------
1055  *
1056  * Add rules for packages possibly selected in by weak dependencies
1057  *
1058  * m: already added solvables
1059  */
1060
1061 void
1062 solver_addrpmrulesforweak(Solver *solv, Map *m)
1063 {
1064   Pool *pool = solv->pool;
1065   Solvable *s;
1066   Id sup, *supp;
1067   int i, n;
1068
1069   /* foreach solvable in pool */
1070   for (i = n = 1; n < pool->nsolvables; i++, n++)
1071     {
1072       if (i == pool->nsolvables)                /* wrap i */
1073         i = 1;
1074       if (MAPTST(m, i))                         /* already added that one */
1075         continue;
1076
1077       s = pool->solvables + i;
1078       if (!s->repo)
1079         continue;
1080       if (s->repo != pool->installed && !pool_installable(pool, s))
1081         continue;       /* only look at installable ones */
1082
1083       sup = 0;
1084       if (s->supplements)
1085         {
1086           /* find possible supplements */
1087           supp = s->repo->idarraydata + s->supplements;
1088           while ((sup = *supp++) != 0)
1089             if (dep_possible(solv, sup, m))
1090               break;
1091         }
1092
1093       /* if nothing found, check for enhances */
1094       if (!sup && s->enhances)
1095         {
1096           supp = s->repo->idarraydata + s->enhances;
1097           while ((sup = *supp++) != 0)
1098             if (dep_possible(solv, sup, m))
1099               break;
1100         }
1101       /* if nothing found, goto next solvables */
1102       if (!sup)
1103         continue;
1104       solver_addrpmrulesforsolvable(solv, s, m);
1105       n = 0;                    /* check all solvables again because we added solvables to m */
1106     }
1107 }
1108
1109
1110 /*-------------------------------------------------------------------
1111  *
1112  * add package rules for possible updates
1113  *
1114  * s: solvable
1115  * m: map of already visited solvables
1116  * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
1117  */
1118
1119 void
1120 solver_addrpmrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
1121 {
1122   Pool *pool = solv->pool;
1123   int i;
1124     /* queue and buffer for it */
1125   Queue qs;
1126   Id qsbuf[64];
1127
1128   queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
1129     /* find update candidates for 's' */
1130   policy_findupdatepackages(solv, s, &qs, allow_all);
1131     /* add rule for 's' if not already done */
1132   if (!MAPTST(m, s - pool->solvables))
1133     solver_addrpmrulesforsolvable(solv, s, m);
1134     /* foreach update candidate, add rule if not already done */
1135   for (i = 0; i < qs.count; i++)
1136     if (!MAPTST(m, qs.elements[i]))
1137       solver_addrpmrulesforsolvable(solv, pool->solvables + qs.elements[i], m);
1138   queue_free(&qs);
1139 }
1140
1141
1142 /***********************************************************************
1143  ***
1144  ***  Update/Feature rule part
1145  ***
1146  ***  Those rules make sure an installed package isn't silently deleted
1147  ***
1148  ***/
1149
1150 static Id
1151 finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
1152 {
1153   Pool *pool = solv->pool;
1154   int i;
1155
1156   policy_findupdatepackages(solv, s, qs, allow_all ? allow_all : 2);
1157   if (!qs->count)
1158     {
1159       if (allow_all)
1160         return 0;       /* orphaned, don't create feature rule */
1161       /* check if this is an orphaned package */
1162       policy_findupdatepackages(solv, s, qs, 1);
1163       if (!qs->count)
1164         return 0;       /* orphaned, don't create update rule */
1165       qs->count = 0;
1166       return -SYSTEMSOLVABLE;   /* supported but not installable */
1167     }
1168   if (allow_all)
1169     return s - pool->solvables;
1170   /* check if it is ok to keep the installed package */
1171   for (i = 0; i < qs->count; i++)
1172     {
1173       Solvable *ns = pool->solvables + qs->elements[i];
1174       if (s->evr == ns->evr && solvable_identical(s, ns))
1175         return s - pool->solvables;
1176     }
1177   /* nope, it must be some other package */
1178   return -SYSTEMSOLVABLE;
1179 }
1180
1181 /* add packages from the dup repositories to the update candidates
1182  * this isn't needed for the global dup mode as all packages are
1183  * from dup repos in that case */
1184 static void
1185 addduppackages(Solver *solv, Solvable *s, Queue *qs)
1186 {
1187   Queue dupqs;
1188   Id p, dupqsbuf[64];
1189   int i;
1190   int oldnoupdateprovide = solv->noupdateprovide;
1191
1192   queue_init_buffer(&dupqs, dupqsbuf, sizeof(dupqsbuf)/sizeof(*dupqsbuf));
1193   solv->noupdateprovide = 1;
1194   policy_findupdatepackages(solv, s, &dupqs, 2);
1195   solv->noupdateprovide = oldnoupdateprovide;
1196   for (i = 0; i < dupqs.count; i++)
1197     {
1198       p = dupqs.elements[i];
1199       if (MAPTST(&solv->dupmap, p))
1200         queue_pushunique(qs, p);
1201     }
1202   queue_free(&dupqs);
1203 }
1204
1205 /*-------------------------------------------------------------------
1206  *
1207  * add rule for update
1208  *   (A|A1|A2|A3...)  An = update candidates for A
1209  *
1210  * s = (installed) solvable
1211  */
1212
1213 void
1214 solver_addupdaterule(Solver *solv, Solvable *s, int allow_all)
1215 {
1216   /* installed packages get a special upgrade allowed rule */
1217   Pool *pool = solv->pool;
1218   Id p, d;
1219   Queue qs;
1220   Id qsbuf[64];
1221
1222   queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
1223   p = s - pool->solvables;
1224   /* find update candidates for 's' */
1225   if (solv->dupmap_all)
1226     p = finddistupgradepackages(solv, s, &qs, allow_all);
1227   else
1228     policy_findupdatepackages(solv, s, &qs, allow_all);
1229   if (!allow_all && !solv->dupmap_all && solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
1230     addduppackages(solv, s, &qs);
1231
1232 #ifdef ENABLE_LINKED_PKGS
1233   if (solv->instbuddy && solv->instbuddy[s - pool->solvables - solv->installed->start])
1234     {
1235       const char *name = pool_id2str(pool, s->name);
1236       if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0)
1237         {
1238           /* a linked pseudo package. As it is linked, we do not need an update rule */
1239           /* nevertheless we set specialupdaters so we can update */
1240           solver_addrule(solv, 0, 0);
1241           if (!allow_all && qs.count)
1242             {
1243               if (p != -SYSTEMSOLVABLE)
1244                 queue_unshift(&qs, p);
1245               if (!solv->specialupdaters)
1246                 solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
1247               solv->specialupdaters[s - pool->solvables - solv->installed->start] = pool_queuetowhatprovides(pool, &qs);
1248             }
1249           queue_free(&qs);
1250           return;
1251         }
1252     }
1253 #endif
1254
1255   if (!allow_all && qs.count && solv->multiversion.size)
1256     {
1257       int i, j;
1258
1259       d = pool_queuetowhatprovides(pool, &qs);
1260       /* filter out all multiversion packages as they don't update */
1261       for (i = j = 0; i < qs.count; i++)
1262         {
1263           if (MAPTST(&solv->multiversion, qs.elements[i]))
1264             {
1265               Solvable *ps = pool->solvables + qs.elements[i];
1266               /* if keepexplicitobsoletes is set and the name is different,
1267                * we assume that there is an obsoletes. XXX: not 100% correct */
1268               if (solv->keepexplicitobsoletes && ps->name != s->name)
1269                 {
1270                   qs.elements[j++] = qs.elements[i];
1271                   continue;
1272                 }
1273               /* it's ok if they have same nevra */
1274               if (ps->name != s->name || ps->evr != s->evr || ps->arch != s->arch)
1275                 continue;
1276             }
1277           qs.elements[j++] = qs.elements[i];
1278         }
1279       if (j < qs.count)
1280         {
1281           if (d && solv->installed && s->repo == solv->installed &&
1282               (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, s - pool->solvables - solv->installed->start))))
1283             {
1284               if (!solv->specialupdaters)
1285                 solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
1286               solv->specialupdaters[s - pool->solvables - solv->installed->start] = d;
1287             }
1288           if (j == 0 && p == -SYSTEMSOLVABLE && solv->dupmap_all)
1289             {
1290               queue_push(&solv->orphaned, s - pool->solvables); /* treat as orphaned */
1291               j = qs.count;
1292             }
1293           qs.count = j;
1294         }
1295       else if (p != -SYSTEMSOLVABLE)
1296         {
1297           /* could fallthrough, but then we would do pool_queuetowhatprovides twice */
1298           queue_free(&qs);
1299           solver_addrule(solv, p, d);   /* allow update of s */
1300           return;
1301         }
1302     }
1303   if (qs.count && p == -SYSTEMSOLVABLE)
1304     p = queue_shift(&qs);
1305   d = qs.count ? pool_queuetowhatprovides(pool, &qs) : 0;
1306   queue_free(&qs);
1307   solver_addrule(solv, p, d);   /* allow update of s */
1308 }
1309
1310 static inline void
1311 disableupdaterule(Solver *solv, Id p)
1312 {
1313   Rule *r;
1314
1315   MAPSET(&solv->noupdate, p - solv->installed->start);
1316   r = solv->rules + solv->updaterules + (p - solv->installed->start);
1317   if (r->p && r->d >= 0)
1318     solver_disablerule(solv, r);
1319   r = solv->rules + solv->featurerules + (p - solv->installed->start);
1320   if (r->p && r->d >= 0)
1321     solver_disablerule(solv, r);
1322   if (solv->bestrules_pkg)
1323     {
1324       int i, ni;
1325       ni = solv->bestrules_end - solv->bestrules;
1326       for (i = 0; i < ni; i++)
1327         if (solv->bestrules_pkg[i] == p)
1328           solver_disablerule(solv, solv->rules + solv->bestrules + i);
1329     }
1330 }
1331
1332 static inline void
1333 reenableupdaterule(Solver *solv, Id p)
1334 {
1335   Pool *pool = solv->pool;
1336   Rule *r;
1337
1338   MAPCLR(&solv->noupdate, p - solv->installed->start);
1339   r = solv->rules + solv->updaterules + (p - solv->installed->start);
1340   if (r->p)
1341     {
1342       if (r->d < 0)
1343         {
1344           solver_enablerule(solv, r);
1345           IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
1346             {
1347               POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
1348               solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
1349             }
1350         }
1351     }
1352   else
1353     {
1354       r = solv->rules + solv->featurerules + (p - solv->installed->start);
1355       if (r->p && r->d < 0)
1356         {
1357           solver_enablerule(solv, r);
1358           IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
1359             {
1360               POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
1361               solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
1362             }
1363         }
1364     }
1365   if (solv->bestrules_pkg)
1366     {
1367       int i, ni;
1368       ni = solv->bestrules_end - solv->bestrules;
1369       for (i = 0; i < ni; i++)
1370         if (solv->bestrules_pkg[i] == p)
1371           solver_enablerule(solv, solv->rules + solv->bestrules + i);
1372     }
1373 }
1374
1375
1376 /***********************************************************************
1377  ***
1378  ***  Infarch rule part
1379  ***
1380  ***  Infarch rules make sure the solver uses the best architecture of
1381  ***  a package if multiple archetectures are available
1382  ***
1383  ***/
1384
1385 void
1386 solver_addinfarchrules(Solver *solv, Map *addedmap)
1387 {
1388   Pool *pool = solv->pool;
1389   Repo *installed = pool->installed;
1390   int first, i, j;
1391   Id p, pp, a, aa, bestarch;
1392   Solvable *s, *ps, *bests;
1393   Queue badq, allowedarchs;
1394   Queue lsq;
1395
1396   queue_init(&badq);
1397   queue_init(&allowedarchs);
1398   queue_init(&lsq);
1399   solv->infarchrules = solv->nrules;
1400   for (i = 1; i < pool->nsolvables; i++)
1401     {
1402       if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
1403         continue;
1404       s = pool->solvables + i;
1405       first = i;
1406       bestarch = 0;
1407       bests = 0;
1408       queue_empty(&allowedarchs);
1409       FOR_PROVIDES(p, pp, s->name)
1410         {
1411           ps = pool->solvables + p;
1412           if (ps->name != s->name || !MAPTST(addedmap, p))
1413             continue;
1414           if (p == i)
1415             first = 0;
1416           if (first)
1417             break;
1418           a = ps->arch;
1419           a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
1420           if (a != 1 && installed && ps->repo == installed)
1421             {
1422               if (!solv->dupmap_all && !(solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
1423                 queue_pushunique(&allowedarchs, ps->arch);      /* also ok to keep this architecture */
1424               continue;         /* ignore installed solvables when calculating the best arch */
1425             }
1426           if (a && a != 1 && (!bestarch || a < bestarch))
1427             {
1428               bestarch = a;
1429               bests = ps;
1430             }
1431         }
1432       if (first)
1433         continue;
1434
1435       /* speed up common case where installed package already has best arch */
1436       if (allowedarchs.count == 1 && bests && allowedarchs.elements[0] == bests->arch)
1437         allowedarchs.count--;   /* installed arch is best */
1438
1439       if (allowedarchs.count && pool->implicitobsoleteusescolors && installed && bestarch)
1440         {
1441           /* need an extra pass for lockstep checking: we only allow to keep an inferior arch
1442            * if the corresponding installed package is not lock-stepped */
1443           queue_empty(&allowedarchs);
1444           FOR_PROVIDES(p, pp, s->name)
1445             {
1446               Id p2, pp2;
1447               ps = pool->solvables + p;
1448               if (ps->name != s->name || ps->repo != installed || !MAPTST(addedmap, p))
1449                 continue;
1450               if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
1451                 continue;
1452               a = ps->arch;
1453               a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
1454               if (!a)
1455                 {
1456                   queue_pushunique(&allowedarchs, ps->arch);    /* strange arch, allow */
1457                   continue;
1458                 }
1459               if (a == 1 || ((a ^ bestarch) & 0xffff0000) == 0)
1460                 continue;
1461               /* have installed package with inferior arch, check if lock-stepped */
1462               FOR_PROVIDES(p2, pp2, s->name)
1463                 {
1464                   Solvable *s2 = pool->solvables + p2;
1465                   Id a2;
1466                   if (p2 == p || s2->name != s->name || s2->evr != pool->solvables[p].evr || s2->arch == pool->solvables[p].arch)
1467                     continue;
1468                   a2 = s2->arch;
1469                   a2 = (a2 <= pool->lastarch) ? pool->id2arch[a2] : 0;
1470                   if (a2 && (a2 == 1 || ((a2 ^ bestarch) & 0xffff0000) == 0))
1471                     break;
1472                 }
1473               if (!p2)
1474                 queue_pushunique(&allowedarchs, ps->arch);
1475             }
1476         }
1477
1478       /* find all bad packages */
1479       queue_empty(&badq);
1480       FOR_PROVIDES(p, pp, s->name)
1481         {
1482           ps = pool->solvables + p;
1483           if (ps->name != s->name || !MAPTST(addedmap, p))
1484             continue;
1485           a = ps->arch;
1486           a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
1487           if (a != 1 && bestarch && ((a ^ bestarch) & 0xffff0000) != 0)
1488             {
1489               if (installed && ps->repo == installed)
1490                 {
1491                   if (pool->implicitobsoleteusescolors)
1492                     queue_push(&badq, p);               /* special lock-step handling, see below */
1493                   continue;     /* always ok to keep an installed package */
1494                 }
1495               for (j = 0; j < allowedarchs.count; j++)
1496                 {
1497                   aa = allowedarchs.elements[j];
1498                   if (ps->arch == aa)
1499                     break;
1500                   aa = (aa <= pool->lastarch) ? pool->id2arch[aa] : 0;
1501                   if (aa && ((a ^ aa) & 0xffff0000) == 0)
1502                     break;      /* compatible */
1503                 }
1504               if (j == allowedarchs.count)
1505                 queue_push(&badq, p);
1506             }
1507         }
1508
1509       /* block all solvables in the badq! */
1510       for (j = 0; j < badq.count; j++)
1511         {
1512           p = badq.elements[j];
1513           /* lock-step */
1514           if (pool->implicitobsoleteusescolors)
1515             {
1516               Id p2;
1517               int haveinstalled = 0;
1518               queue_empty(&lsq);
1519               FOR_PROVIDES(p2, pp, s->name)
1520                 {
1521                   Solvable *s2 = pool->solvables + p2;
1522                   if (p2 == p || s2->name != s->name || s2->evr != pool->solvables[p].evr || s2->arch == pool->solvables[p].arch)
1523                     continue;
1524                   a = s2->arch;
1525                   a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
1526                   if (a && (a == 1 || ((a ^ bestarch) & 0xffff000) == 0))
1527                     {
1528                       queue_push(&lsq, p2);
1529                       if (installed && s2->repo == installed)
1530                         haveinstalled = 1;
1531                     }
1532                 }
1533               if (installed && pool->solvables[p].repo == installed && !haveinstalled)
1534                 continue;       /* installed package not in lock-step */
1535             }
1536           solver_addrule(solv, -p, lsq.count ? pool_queuetowhatprovides(pool, &lsq) : 0);
1537         }
1538     }
1539   queue_free(&lsq);
1540   queue_free(&badq);
1541   queue_free(&allowedarchs);
1542   solv->infarchrules_end = solv->nrules;
1543 }
1544
1545 static inline void
1546 disableinfarchrule(Solver *solv, Id name)
1547 {
1548   Pool *pool = solv->pool;
1549   Rule *r;
1550   int i;
1551   for (i = solv->infarchrules, r = solv->rules + i; i < solv->infarchrules_end; i++, r++)
1552     {
1553       if (r->p < 0 && r->d >= 0 && pool->solvables[-r->p].name == name)
1554         solver_disablerule(solv, r);
1555     }
1556 }
1557
1558 static inline void
1559 reenableinfarchrule(Solver *solv, Id name)
1560 {
1561   Pool *pool = solv->pool;
1562   Rule *r;
1563   int i;
1564   for (i = solv->infarchrules, r = solv->rules + i; i < solv->infarchrules_end; i++, r++)
1565     {
1566       if (r->p < 0 && r->d < 0 && pool->solvables[-r->p].name == name)
1567         {
1568           solver_enablerule(solv, r);
1569           IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
1570             {
1571               POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
1572               solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
1573             }
1574         }
1575     }
1576 }
1577
1578
1579 /***********************************************************************
1580  ***
1581  ***  Dup rule part
1582  ***
1583  ***  Dup rules make sure a package is selected from the specified dup
1584  ***  repositories if an update candidate is included in one of them.
1585  ***
1586  ***/
1587
1588 static inline void
1589 add_cleandeps_package(Solver *solv, Id p)
1590 {
1591   if (!solv->cleandeps_updatepkgs)
1592     {
1593       solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
1594       queue_init(solv->cleandeps_updatepkgs);
1595     }
1596   queue_pushunique(solv->cleandeps_updatepkgs, p);
1597 }
1598
1599 static inline void
1600 solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted)
1601 {
1602   Pool *pool = solv->pool;
1603   Solvable *ps, *s = pool->solvables + p;
1604   Repo *installed = solv->installed;
1605   Id pi, pip, obs, *obsp;
1606
1607   MAPSET(&solv->dupinvolvedmap, p);
1608   if (targeted)
1609     MAPSET(&solv->dupmap, p);
1610   FOR_PROVIDES(pi, pip, s->name)
1611     {
1612       ps = pool->solvables + pi;
1613       if (ps->name != s->name)
1614         continue;
1615       MAPSET(&solv->dupinvolvedmap, pi);
1616       if (targeted && ps->repo == installed && solv->obsoletes && solv->obsoletes[pi - installed->start])
1617         {
1618           Id *opp, pi2;
1619           for (opp = solv->obsoletes_data + solv->obsoletes[pi - installed->start]; (pi2 = *opp++) != 0;)
1620             if (pool->solvables[pi2].repo != installed)
1621               MAPSET(&solv->dupinvolvedmap, pi2);
1622         }
1623       if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0)
1624         {
1625           if (!solv->bestupdatemap.size)
1626             map_grow(&solv->bestupdatemap, installed->end - installed->start);
1627           MAPSET(&solv->bestupdatemap, pi - installed->start);
1628         }
1629       if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
1630         add_cleandeps_package(solv, pi);
1631       if (!targeted && ps->repo != installed)
1632         MAPSET(&solv->dupmap, pi);
1633     }
1634   if (s->repo == installed && solv->obsoletes && solv->obsoletes[p - installed->start])
1635     {
1636       Id *opp;
1637       for (opp = solv->obsoletes_data + solv->obsoletes[p - installed->start]; (pi = *opp++) != 0;)
1638         {
1639           ps = pool->solvables + pi;
1640           if (ps->repo == installed)
1641             continue;
1642           MAPSET(&solv->dupinvolvedmap, pi);
1643           if (!targeted)
1644             MAPSET(&solv->dupmap, pi);
1645         }
1646     }
1647   if (targeted && s->repo != installed && s->obsoletes)
1648     {
1649       /* XXX: check obsoletes/provides combination */
1650       obsp = s->repo->idarraydata + s->obsoletes;
1651       while ((obs = *obsp++) != 0)
1652         {
1653           FOR_PROVIDES(pi, pip, obs)
1654             {
1655               Solvable *ps = pool->solvables + pi;
1656               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
1657                 continue;
1658               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
1659                 continue;
1660               MAPSET(&solv->dupinvolvedmap, pi);
1661               if (targeted && ps->repo == installed && solv->obsoletes && solv->obsoletes[pi - installed->start])
1662                 {
1663                   Id *opp, pi2;
1664                   for (opp = solv->obsoletes_data + solv->obsoletes[pi - installed->start]; (pi2 = *opp++) != 0;)
1665                     if (pool->solvables[pi2].repo != installed)
1666                       MAPSET(&solv->dupinvolvedmap, pi2);
1667                 }
1668               if (ps->repo == installed && (how & SOLVER_FORCEBEST) != 0)
1669                 {
1670                   if (!solv->bestupdatemap.size)
1671                     map_grow(&solv->bestupdatemap, installed->end - installed->start);
1672                   MAPSET(&solv->bestupdatemap, pi - installed->start);
1673                 }
1674               if (ps->repo == installed && (how & SOLVER_CLEANDEPS) != 0)
1675                 add_cleandeps_package(solv, pi);
1676             }
1677         }
1678     }
1679 }
1680
1681 void
1682 solver_createdupmaps(Solver *solv)
1683 {
1684   Queue *job = &solv->job;
1685   Pool *pool = solv->pool;
1686   Repo *installed = solv->installed;
1687   Id select, how, what, p, pp;
1688   Solvable *s;
1689   int i, targeted;
1690
1691   map_init(&solv->dupmap, pool->nsolvables);
1692   map_init(&solv->dupinvolvedmap, pool->nsolvables);
1693   for (i = 0; i < job->count; i += 2)
1694     {
1695       how = job->elements[i];
1696       select = job->elements[i] & SOLVER_SELECTMASK;
1697       what = job->elements[i + 1];
1698       switch (how & SOLVER_JOBMASK)
1699         {
1700         case SOLVER_DISTUPGRADE:
1701           if (select == SOLVER_SOLVABLE_REPO)
1702             {
1703               Repo *repo;
1704               if (what <= 0 || what > pool->nrepos)
1705                 break;
1706               repo = pool_id2repo(pool, what);
1707               if (!repo)
1708                 break;
1709               if (repo != installed && !(how & SOLVER_TARGETED) && solv->noautotarget)
1710                 break;
1711               targeted = repo != installed || (how & SOLVER_TARGETED) != 0;
1712               FOR_REPO_SOLVABLES(repo, p, s)
1713                 {
1714                   if (repo != installed && !pool_installable(pool, s))
1715                     continue;
1716                   solver_addtodupmaps(solv, p, how, targeted);
1717                 }
1718             }
1719           else
1720             {
1721               targeted = how & SOLVER_TARGETED ? 1 : 0;
1722               if (installed && !targeted && !solv->noautotarget)
1723                 {
1724                   FOR_JOB_SELECT(p, pp, select, what)
1725                     if (pool->solvables[p].repo == installed)
1726                       break;
1727                   targeted = p == 0;
1728                 }
1729               else if (!installed && !solv->noautotarget)
1730                 targeted = 1;
1731               FOR_JOB_SELECT(p, pp, select, what)
1732                 {
1733                   Solvable *s = pool->solvables + p;
1734                   if (!s->repo)
1735                     continue;
1736                   if (s->repo != installed && !targeted)
1737                     continue;
1738                   if (s->repo != installed && !pool_installable(pool, s))
1739                     continue;
1740                   solver_addtodupmaps(solv, p, how, targeted);
1741                 }
1742             }
1743           break;
1744         default:
1745           break;
1746         }
1747     }
1748   MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE);
1749 }
1750
1751 void
1752 solver_freedupmaps(Solver *solv)
1753 {
1754   map_free(&solv->dupmap);
1755   /* we no longer free solv->dupinvolvedmap as we need it in
1756    * policy's priority pruning code. sigh. */
1757 }
1758
1759 void
1760 solver_addduprules(Solver *solv, Map *addedmap)
1761 {
1762   Pool *pool = solv->pool;
1763   Id p, pp;
1764   Solvable *s, *ps;
1765   int first, i;
1766
1767   solv->duprules = solv->nrules;
1768   for (i = 1; i < pool->nsolvables; i++)
1769     {
1770       if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
1771         continue;
1772       s = pool->solvables + i;
1773       first = i;
1774       FOR_PROVIDES(p, pp, s->name)
1775         {
1776           ps = pool->solvables + p;
1777           if (ps->name != s->name || !MAPTST(addedmap, p))
1778             continue;
1779           if (p == i)
1780             first = 0;
1781           if (first)
1782             break;
1783           if (!MAPTST(&solv->dupinvolvedmap, p))
1784             continue;
1785           if (solv->installed && ps->repo == solv->installed)
1786             {
1787               if (!solv->updatemap.size)
1788                 map_grow(&solv->updatemap, solv->installed->end - solv->installed->start);
1789               MAPSET(&solv->updatemap, p - solv->installed->start);
1790               if (!MAPTST(&solv->dupmap, p))
1791                 {
1792                   Id ip, ipp;
1793                   /* is installed identical to a good one? */
1794                   FOR_PROVIDES(ip, ipp, ps->name)
1795                     {
1796                       Solvable *is = pool->solvables + ip;
1797                       if (!MAPTST(&solv->dupmap, ip))
1798                         continue;
1799                       if (is->evr == ps->evr && solvable_identical(ps, is))
1800                         break;
1801                     }
1802                   if (!ip)
1803                     solver_addrule(solv, -p, 0);        /* no match, sorry */
1804                   else
1805                     MAPSET(&solv->dupmap, p);           /* for best rules processing */
1806                 }
1807             }
1808           else if (!MAPTST(&solv->dupmap, p))
1809             solver_addrule(solv, -p, 0);
1810         }
1811     }
1812   solv->duprules_end = solv->nrules;
1813 }
1814
1815
1816 static inline void
1817 disableduprule(Solver *solv, Id name)
1818 {
1819   Pool *pool = solv->pool;
1820   Rule *r;
1821   int i;
1822   for (i = solv->duprules, r = solv->rules + i; i < solv->duprules_end; i++, r++)
1823     {
1824       if (r->p < 0 && r->d >= 0 && pool->solvables[-r->p].name == name)
1825         solver_disablerule(solv, r);
1826     }
1827 }
1828
1829 static inline void
1830 reenableduprule(Solver *solv, Id name)
1831 {
1832   Pool *pool = solv->pool;
1833   Rule *r;
1834   int i;
1835   for (i = solv->duprules, r = solv->rules + i; i < solv->duprules_end; i++, r++)
1836     {
1837       if (r->p < 0 && r->d < 0 && pool->solvables[-r->p].name == name)
1838         {
1839           solver_enablerule(solv, r);
1840           IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
1841             {
1842               POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "@@@ re-enabling ");
1843               solver_printruleclass(solv, SOLV_DEBUG_SOLUTIONS, r);
1844             }
1845         }
1846     }
1847 }
1848
1849
1850 /***********************************************************************
1851  ***
1852  ***  Policy rule disabling/reenabling
1853  ***
1854  ***  Disable all policy rules that conflict with our jobs. If a job
1855  ***  gets disabled later on, reenable the involved policy rules again.
1856  ***
1857  ***/
1858
1859 #define DISABLE_UPDATE  1
1860 #define DISABLE_INFARCH 2
1861 #define DISABLE_DUP     3
1862
1863 /*
1864  * add all installed packages that package p obsoletes to Queue q.
1865  * Package p is not installed. Also, we know that if
1866  * solv->keepexplicitobsoletes is not set, p is not in the multiversion map.
1867  * Entries may get added multiple times.
1868  */
1869 static void
1870 add_obsoletes(Solver *solv, Id p, Queue *q)
1871 {
1872   Pool *pool = solv->pool;
1873   Repo *installed = solv->installed;
1874   Id p2, pp2;
1875   Solvable *s = pool->solvables + p;
1876   Id obs, *obsp;
1877   Id lastp2 = 0;
1878
1879   if (!solv->keepexplicitobsoletes || !(solv->multiversion.size && MAPTST(&solv->multiversion, p)))
1880     {
1881       FOR_PROVIDES(p2, pp2, s->name)
1882         {
1883           Solvable *ps = pool->solvables + p2;
1884           if (ps->repo != installed)
1885             continue;
1886           if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
1887             continue;
1888           if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
1889             continue;
1890           queue_push(q, p2);
1891           lastp2 = p2;
1892         }
1893     }
1894   if (!s->obsoletes)
1895     return;
1896   obsp = s->repo->idarraydata + s->obsoletes;
1897   while ((obs = *obsp++) != 0)
1898     FOR_PROVIDES(p2, pp2, obs)
1899       {
1900         Solvable *ps = pool->solvables + p2;
1901         if (ps->repo != installed)
1902           continue;
1903         if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
1904           continue;
1905         if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
1906           continue;
1907         if (p2 == lastp2)
1908           continue;
1909         queue_push(q, p2);
1910         lastp2 = p2;
1911       }
1912 }
1913
1914 /*
1915  * Call add_obsoletes and intersect the result with the
1916  * elements in Queue q starting at qstart.
1917  * Assumes that it's the first call if qstart == q->count.
1918  * May use auxillary map m for the intersection process, all
1919  * elements of q starting at qstart must have their bit cleared.
1920  * (This is also true after the function returns.)
1921  */
1922 static void
1923 intersect_obsoletes(Solver *solv, Id p, Queue *q, int qstart, Map *m)
1924 {
1925   int i, j;
1926   int qcount = q->count;
1927
1928   add_obsoletes(solv, p, q);
1929   if (qcount == qstart)
1930     return;     /* first call */
1931   if (qcount == q->count)
1932     j = qstart; 
1933   else if (qcount == qstart + 1)
1934     {
1935       /* easy if there's just one element */
1936       j = qstart;
1937       for (i = qcount; i < q->count; i++)
1938         if (q->elements[i] == q->elements[qstart])
1939           {
1940             j++;        /* keep the element */
1941             break;
1942           }
1943     }
1944   else if (!m->size && q->count - qstart <= 8)
1945     {
1946       /* faster than a map most of the time */
1947       int k;
1948       for (i = j = qstart; i < qcount; i++)
1949         {
1950           Id ip = q->elements[i];
1951           for (k = qcount; k < q->count; k++)
1952             if (q->elements[k] == ip)
1953               {
1954                 q->elements[j++] = ip;
1955                 break;
1956               }
1957         }
1958     }
1959   else
1960     {
1961       /* for the really pathologic cases we use the map */
1962       Repo *installed = solv->installed;
1963       if (!m->size)
1964         map_init(m, installed->end - installed->start);
1965       for (i = qcount; i < q->count; i++)
1966         MAPSET(m, q->elements[i] - installed->start);
1967       for (i = j = qstart; i < qcount; i++)
1968         if (MAPTST(m, q->elements[i] - installed->start))
1969           {
1970             MAPCLR(m, q->elements[i] - installed->start);
1971             q->elements[j++] = q->elements[i];
1972           }
1973     }
1974   queue_truncate(q, j);
1975 }
1976
1977 static void
1978 jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
1979 {
1980   Pool *pool = solv->pool;
1981   Id select, p, pp;
1982   Repo *installed;
1983   Solvable *s;
1984   int i, j, set, qstart;
1985   Map omap;
1986
1987   installed = solv->installed;
1988   select = how & SOLVER_SELECTMASK;
1989   switch (how & SOLVER_JOBMASK)
1990     {
1991     case SOLVER_INSTALL:
1992       set = how & SOLVER_SETMASK;
1993       if (!(set & SOLVER_NOAUTOSET))
1994         {
1995           /* automatically add set bits by analysing the job */
1996           if (select == SOLVER_SOLVABLE_NAME)
1997             set |= SOLVER_SETNAME;
1998           if (select == SOLVER_SOLVABLE)
1999             set |= SOLVER_SETNAME | SOLVER_SETARCH | SOLVER_SETVENDOR | SOLVER_SETREPO | SOLVER_SETEVR;
2000           else if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && ISRELDEP(what))
2001             {
2002               Reldep *rd = GETRELDEP(pool, what);
2003               if (rd->flags == REL_EQ && select == SOLVER_SOLVABLE_NAME)
2004                 {
2005                   if (pool->disttype != DISTTYPE_DEB)
2006                     {
2007                       const char *rel = strrchr(pool_id2str(pool, rd->evr), '-');
2008                       set |= rel ? SOLVER_SETEVR : SOLVER_SETEV;
2009                     }
2010                   else
2011                     set |= SOLVER_SETEVR;
2012                 }
2013               if (rd->flags <= 7 && ISRELDEP(rd->name))
2014                 rd = GETRELDEP(pool, rd->name);
2015               if (rd->flags == REL_ARCH)
2016                 set |= SOLVER_SETARCH;
2017             }
2018         }
2019       else
2020         set &= ~SOLVER_NOAUTOSET;
2021       if (!set)
2022         return;
2023       if ((set & SOLVER_SETARCH) != 0 && solv->infarchrules != solv->infarchrules_end)
2024         {
2025           if (select == SOLVER_SOLVABLE)
2026             queue_push2(q, DISABLE_INFARCH, pool->solvables[what].name);
2027           else
2028             {
2029               int qcnt = q->count;
2030               /* does not work for SOLVER_SOLVABLE_ALL and SOLVER_SOLVABLE_REPO, but
2031                  they are not useful for SOLVER_INSTALL jobs anyway */
2032               FOR_JOB_SELECT(p, pp, select, what)
2033                 {
2034                   s = pool->solvables + p;
2035                   /* unify names */
2036                   for (i = qcnt; i < q->count; i += 2)
2037                     if (q->elements[i + 1] == s->name)
2038                       break;
2039                   if (i < q->count)
2040                     continue;
2041                   queue_push2(q, DISABLE_INFARCH, s->name);
2042                 }
2043             }
2044         }
2045       if ((set & SOLVER_SETREPO) != 0 && solv->duprules != solv->duprules_end)
2046         {
2047           if (select == SOLVER_SOLVABLE)
2048             queue_push2(q, DISABLE_DUP, pool->solvables[what].name);
2049           else
2050             {
2051               int qcnt = q->count;
2052               FOR_JOB_SELECT(p, pp, select, what)
2053                 {
2054                   s = pool->solvables + p;
2055                   /* unify names */
2056                   for (i = qcnt; i < q->count; i += 2)
2057                     if (q->elements[i + 1] == s->name)
2058                       break;
2059                   if (i < q->count)
2060                     continue;
2061                   queue_push2(q, DISABLE_DUP, s->name);
2062                 }
2063             }
2064         }
2065       if (!installed || installed->end == installed->start)
2066         return;
2067       /* now the hard part: disable some update rules */
2068
2069       /* first check if we have multiversion or installed packages in the job */
2070       i = j = 0;
2071       FOR_JOB_SELECT(p, pp, select, what)
2072         {
2073           if (pool->solvables[p].repo == installed)
2074             j = p;
2075           else if (solv->multiversion.size && MAPTST(&solv->multiversion, p) && !solv->keepexplicitobsoletes)
2076             return;
2077           i++;
2078         }
2079       if (j)    /* have installed packages */
2080         {
2081           /* this is for dupmap_all jobs, it can go away if we create
2082            * duprules for them */
2083           if (i == 1 && (set & SOLVER_SETREPO) != 0)
2084             queue_push2(q, DISABLE_UPDATE, j);
2085           return;
2086         }
2087
2088       omap.size = 0;
2089       qstart = q->count;
2090       FOR_JOB_SELECT(p, pp, select, what)
2091         {
2092           intersect_obsoletes(solv, p, q, qstart, &omap);
2093           if (q->count == qstart)
2094             break;
2095         }
2096       if (omap.size)
2097         map_free(&omap);
2098
2099       if (qstart == q->count)
2100         return;         /* nothing to prune */
2101
2102       /* convert result to (DISABLE_UPDATE, p) pairs */
2103       i = q->count;
2104       for (j = qstart; j < i; j++)
2105         queue_push(q, q->elements[j]);
2106       for (j = qstart; j < q->count; j += 2)
2107         {
2108           q->elements[j] = DISABLE_UPDATE;
2109           q->elements[j + 1] = q->elements[i++];
2110         }
2111
2112       /* now that we know which installed packages are obsoleted check each of them */
2113       if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
2114         return;         /* all is set, nothing to do */
2115
2116       for (i = j = qstart; i < q->count; i += 2)
2117         {
2118           Solvable *is = pool->solvables + q->elements[i + 1];
2119           FOR_JOB_SELECT(p, pp, select, what)
2120             {
2121               int illegal = 0;
2122               s = pool->solvables + p;
2123               if ((set & SOLVER_SETEVR) != 0)
2124                 illegal |= POLICY_ILLEGAL_DOWNGRADE;    /* ignore */
2125               if ((set & SOLVER_SETNAME) != 0)
2126                 illegal |= POLICY_ILLEGAL_NAMECHANGE;   /* ignore */
2127               if ((set & SOLVER_SETARCH) != 0)
2128                 illegal |= POLICY_ILLEGAL_ARCHCHANGE;   /* ignore */
2129               if ((set & SOLVER_SETVENDOR) != 0)
2130                 illegal |= POLICY_ILLEGAL_VENDORCHANGE; /* ignore */
2131               illegal = policy_is_illegal(solv, is, s, illegal);
2132               if (illegal && illegal == POLICY_ILLEGAL_DOWNGRADE && (set & SOLVER_SETEV) != 0)
2133                 {
2134                   /* it's ok if the EV is different */
2135                   if (pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE_EVONLY) != 0)
2136                     illegal = 0;
2137                 }
2138               if (illegal)
2139                 break;
2140             }
2141           if (!p)
2142             {   
2143               /* no package conflicts with the update rule */
2144               /* thus keep the DISABLE_UPDATE */
2145               q->elements[j + 1] = q->elements[i + 1];
2146               j += 2;
2147             }
2148         }
2149       queue_truncate(q, j);
2150       return;
2151
2152     case SOLVER_ERASE:
2153       if (!installed)
2154         break;
2155       if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
2156         FOR_REPO_SOLVABLES(installed, p, s)
2157           queue_push2(q, DISABLE_UPDATE, p);
2158       FOR_JOB_SELECT(p, pp, select, what)
2159         if (pool->solvables[p].repo == installed)
2160           {
2161             queue_push2(q, DISABLE_UPDATE, p);
2162 #ifdef ENABLE_LINKED_PKGS
2163             if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
2164               queue_push2(q, DISABLE_UPDATE, solv->instbuddy[p - installed->start]);
2165 #endif
2166           }
2167       return;
2168     default:
2169       return;
2170     }
2171 }
2172
2173 /* disable all policy rules that are in conflict with our job list */
2174 void
2175 solver_disablepolicyrules(Solver *solv)
2176 {
2177   Queue *job = &solv->job;
2178   int i, j;
2179   Queue allq;
2180   Rule *r;
2181   Id lastjob = -1;
2182   Id allqbuf[128];
2183
2184   queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
2185
2186   for (i = solv->jobrules; i < solv->jobrules_end; i++)
2187     {
2188       r = solv->rules + i;
2189       if (r->d < 0)     /* disabled? */
2190         continue;
2191       j = solv->ruletojob.elements[i - solv->jobrules];
2192       if (j == lastjob)
2193         continue;
2194       lastjob = j;
2195       jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
2196     }
2197   if (solv->cleandepsmap.size)
2198     {
2199       solver_createcleandepsmap(solv, &solv->cleandepsmap, 0);
2200       for (i = solv->installed->start; i < solv->installed->end; i++)
2201         if (MAPTST(&solv->cleandepsmap, i - solv->installed->start))
2202           queue_push2(&allq, DISABLE_UPDATE, i);
2203     }
2204   MAPZERO(&solv->noupdate);
2205   for (i = 0; i < allq.count; i += 2)
2206     {
2207       Id type = allq.elements[i], arg = allq.elements[i + 1];
2208       switch(type)
2209         {
2210         case DISABLE_UPDATE:
2211           disableupdaterule(solv, arg);
2212           break;
2213         case DISABLE_INFARCH:
2214           disableinfarchrule(solv, arg);
2215           break;
2216         case DISABLE_DUP:
2217           disableduprule(solv, arg);
2218           break;
2219         default:
2220           break;
2221         }
2222     }
2223   queue_free(&allq);
2224 }
2225
2226 /* we just disabled job #jobidx, now reenable all policy rules that were
2227  * disabled because of this job */
2228 void
2229 solver_reenablepolicyrules(Solver *solv, int jobidx)
2230 {
2231   Queue *job = &solv->job;
2232   int i, j, k, ai;
2233   Queue q, allq;
2234   Rule *r;
2235   Id lastjob = -1;
2236   Id qbuf[32], allqbuf[32];
2237
2238   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
2239   jobtodisablelist(solv, job->elements[jobidx - 1], job->elements[jobidx], &q);
2240   if (!q.count)
2241     {
2242       queue_free(&q);
2243       return;
2244     }
2245   /* now remove everything from q that is disabled by other jobs */
2246
2247   /* first remove cleandeps packages, they count as DISABLE_UPDATE */
2248   if (solv->cleandepsmap.size)
2249     {
2250       solver_createcleandepsmap(solv, &solv->cleandepsmap, 0);
2251       for (j = k = 0; j < q.count; j += 2)
2252         {
2253           if (q.elements[j] == DISABLE_UPDATE)
2254             {
2255               Id p = q.elements[j + 1];
2256               if (p >= solv->installed->start && p < solv->installed->end && MAPTST(&solv->cleandepsmap, p - solv->installed->start))
2257                 continue;       /* remove element from q */
2258             }
2259           q.elements[k++] = q.elements[j];
2260           q.elements[k++] = q.elements[j + 1];
2261         }
2262       q.count = k;
2263       if (!q.count)
2264         {
2265           queue_free(&q);
2266           return;
2267         }
2268     }
2269
2270   /* now go through the disable list of all other jobs */
2271   queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
2272   for (i = solv->jobrules; i < solv->jobrules_end; i++)
2273     {
2274       r = solv->rules + i;
2275       if (r->d < 0)     /* disabled? */
2276         continue;
2277       j = solv->ruletojob.elements[i - solv->jobrules];
2278       if (j == lastjob)
2279         continue;
2280       lastjob = j;
2281       jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
2282       if (!allq.count)
2283         continue;
2284       /* remove all elements in allq from q */
2285       for (j = k = 0; j < q.count; j += 2)
2286         {
2287           Id type = q.elements[j], arg = q.elements[j + 1];
2288           for (ai = 0; ai < allq.count; ai += 2)
2289             if (allq.elements[ai] == type && allq.elements[ai + 1] == arg)
2290               break;
2291           if (ai < allq.count)
2292             continue;   /* found it in allq, remove element from q */
2293           q.elements[k++] = q.elements[j];
2294           q.elements[k++] = q.elements[j + 1];
2295         }
2296       q.count = k;
2297       if (!q.count)
2298         {
2299           queue_free(&q);
2300           queue_free(&allq);
2301           return;
2302         }
2303       queue_empty(&allq);
2304     }
2305   queue_free(&allq);
2306
2307   /* now re-enable anything that's left in q */
2308   for (j = 0; j < q.count; j += 2)
2309     {
2310       Id type = q.elements[j], arg = q.elements[j + 1];
2311       switch(type)
2312         {
2313         case DISABLE_UPDATE:
2314           reenableupdaterule(solv, arg);
2315           break;
2316         case DISABLE_INFARCH:
2317           reenableinfarchrule(solv, arg);
2318           break;
2319         case DISABLE_DUP:
2320           reenableduprule(solv, arg);
2321           break;
2322         }
2323     }
2324   queue_free(&q);
2325 }
2326
2327 /* we just removed a package from the cleandeps map, now reenable all policy rules that were
2328  * disabled because of this */
2329 void
2330 solver_reenablepolicyrules_cleandeps(Solver *solv, Id pkg)
2331 {
2332   Queue *job = &solv->job;
2333   int i, j;
2334   Queue allq;
2335   Rule *r;
2336   Id lastjob = -1;
2337   Id allqbuf[128];
2338
2339   queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
2340   for (i = solv->jobrules; i < solv->jobrules_end; i++)
2341     {
2342       r = solv->rules + i;
2343       if (r->d < 0)     /* disabled? */
2344         continue;
2345       j = solv->ruletojob.elements[i - solv->jobrules];
2346       if (j == lastjob)
2347         continue;
2348       lastjob = j;
2349       jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
2350     }
2351   for (i = 0; i < allq.count; i += 2)
2352     if (allq.elements[i] == DISABLE_UPDATE && allq.elements[i + 1] == pkg)
2353       break;
2354   if (i == allq.count)
2355     reenableupdaterule(solv, pkg);
2356   queue_free(&allq);
2357 }
2358
2359
2360 /***********************************************************************
2361  ***
2362  ***  Rule info part, tell the user what the rule is about.
2363  ***
2364  ***/
2365
2366 static void
2367 addrpmruleinfo(Solver *solv, Id p, Id d, int type, Id dep)
2368 {
2369   Pool *pool = solv->pool;
2370   Rule *r;
2371   Id w2, op, od, ow2;
2372
2373   /* check if this creates the rule we're searching for */
2374   r = solv->rules + solv->ruleinfoq->elements[0];
2375   op = r->p;
2376   od = r->d < 0 ? -r->d - 1 : r->d;
2377   ow2 = 0;
2378
2379   /* normalize */
2380   w2 = d > 0 ? 0 : d;
2381   if (p < 0 && d > 0 && (!pool->whatprovidesdata[d] || !pool->whatprovidesdata[d + 1]))
2382     {
2383       w2 = pool->whatprovidesdata[d];
2384       d = 0;
2385     }
2386   if (p > 0 && d < 0)           /* this hack is used for package links and complex deps */
2387     {
2388       w2 = p;
2389       p = d;
2390     }
2391
2392   if (d > 0)
2393     {
2394       if (p != op && !od)
2395         return;
2396       if (d != od)
2397         {
2398           Id *dp = pool->whatprovidesdata + d;
2399           Id *odp = pool->whatprovidesdata + od;
2400           while (*dp)
2401             if (*dp++ != *odp++)
2402               return;
2403           if (*odp)
2404             return;
2405         }
2406       w2 = 0;
2407       /* handle multiversion conflict rules */
2408       if (p < 0 && pool->whatprovidesdata[d] < 0)
2409         {
2410           w2 = pool->whatprovidesdata[d];
2411           /* XXX: free memory */
2412         }
2413     }
2414   else
2415     {
2416       if (od)
2417         return;
2418       ow2 = r->w2;
2419       if (p > w2)
2420         {
2421           if (w2 != op || p != ow2)
2422             return;
2423         }
2424       else
2425         {
2426           if (p != op || w2 != ow2)
2427             return;
2428         }
2429     }
2430   /* yep, rule matches. record info */
2431   queue_push(solv->ruleinfoq, type);
2432   if (type == SOLVER_RULE_RPM_SAME_NAME)
2433     {
2434       /* we normalize same name order */
2435       queue_push(solv->ruleinfoq, op < 0 ? -op : 0);
2436       queue_push(solv->ruleinfoq, ow2 < 0 ? -ow2 : 0);
2437     }
2438   else
2439     {
2440       queue_push(solv->ruleinfoq, p < 0 ? -p : 0);
2441       queue_push(solv->ruleinfoq, w2 < 0 ? -w2 : 0);
2442     }
2443   queue_push(solv->ruleinfoq, dep);
2444 }
2445
2446 static int
2447 solver_allruleinfos_cmp(const void *ap, const void *bp, void *dp)
2448 {
2449   const Id *a = ap, *b = bp;
2450   int r;
2451
2452   r = a[0] - b[0];
2453   if (r)
2454     return r;
2455   r = a[1] - b[1];
2456   if (r)
2457     return r;
2458   r = a[2] - b[2];
2459   if (r)
2460     return r;
2461   r = a[3] - b[3];
2462   if (r)
2463     return r;
2464   return 0;
2465 }
2466
2467 static void
2468 getrpmruleinfos(Solver *solv, Rule *r, Queue *rq)
2469 {
2470   Pool *pool = solv->pool;
2471   Id l, pp;
2472   if (r->p >= 0)
2473     return;
2474   queue_push(rq, r - solv->rules);      /* push the rule we're interested in */
2475   solv->ruleinfoq = rq;
2476   FOR_RULELITERALS(l, pp, r)
2477     {
2478       if (l >= 0)
2479         break;
2480       solver_addrpmrulesforsolvable(solv, pool->solvables - l, 0);
2481     }
2482 #ifdef ENABLE_LINKED_PKGS
2483   FOR_RULELITERALS(l, pp, r)
2484     {
2485       if (l < 0 || l != r->p)
2486         break;
2487       if (!strchr(pool_id2str(pool, pool->solvables[l].name), ':') || !has_package_link(pool, pool->solvables + l))
2488         break;
2489       add_package_link(solv, pool->solvables + l, 0, 0);
2490     }
2491 #endif
2492   solv->ruleinfoq = 0;
2493   queue_shift(rq);
2494 }
2495
2496 int
2497 solver_allruleinfos(Solver *solv, Id rid, Queue *rq)
2498 {
2499   Rule *r = solv->rules + rid;
2500   int i, j;
2501
2502   queue_empty(rq);
2503   if (rid <= 0 || rid >= solv->rpmrules_end)
2504     {
2505       Id type, from, to, dep;
2506       type = solver_ruleinfo(solv, rid, &from, &to, &dep);
2507       queue_push(rq, type);
2508       queue_push(rq, from);
2509       queue_push(rq, to);
2510       queue_push(rq, dep);
2511       return 1;
2512     }
2513   getrpmruleinfos(solv, r, rq);
2514   /* now sort & unify em */
2515   if (!rq->count)
2516     return 0;
2517   solv_sort(rq->elements, rq->count / 4, 4 * sizeof(Id), solver_allruleinfos_cmp, 0);
2518   /* throw out identical entries */
2519   for (i = j = 0; i < rq->count; i += 4)
2520     {
2521       if (j)
2522         {
2523           if (rq->elements[i] == rq->elements[j - 4] &&
2524               rq->elements[i + 1] == rq->elements[j - 3] &&
2525               rq->elements[i + 2] == rq->elements[j - 2] &&
2526               rq->elements[i + 3] == rq->elements[j - 1])
2527             continue;
2528         }
2529       rq->elements[j++] = rq->elements[i];
2530       rq->elements[j++] = rq->elements[i + 1];
2531       rq->elements[j++] = rq->elements[i + 2];
2532       rq->elements[j++] = rq->elements[i + 3];
2533     }
2534   rq->count = j;
2535   return j / 4;
2536 }
2537
2538 SolverRuleinfo
2539 solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
2540 {
2541   Pool *pool = solv->pool;
2542   Rule *r = solv->rules + rid;
2543   SolverRuleinfo type = SOLVER_RULE_UNKNOWN;
2544
2545   if (fromp)
2546     *fromp = 0;
2547   if (top)
2548     *top = 0;
2549   if (depp)
2550     *depp = 0;
2551   if (rid > 0 && rid < solv->rpmrules_end)
2552     {
2553       Queue rq;
2554       int i;
2555
2556       if (r->p >= 0)
2557         return SOLVER_RULE_RPM;
2558       if (fromp)
2559         *fromp = -r->p;
2560       queue_init(&rq);
2561       getrpmruleinfos(solv, r, &rq);
2562       type = SOLVER_RULE_RPM;
2563       for (i = 0; i < rq.count; i += 4)
2564         {
2565           Id qt, qo, qp, qd;
2566           qt = rq.elements[i];
2567           qp = rq.elements[i + 1];
2568           qo = rq.elements[i + 2];
2569           qd = rq.elements[i + 3];
2570           if (type == SOLVER_RULE_RPM || type > qt)
2571             {
2572               type = qt;
2573               if (fromp)
2574                 *fromp = qp;
2575               if (top)
2576                 *top = qo;
2577               if (depp)
2578                 *depp = qd;
2579             }
2580         }
2581       queue_free(&rq);
2582       return type;
2583     }
2584   if (rid >= solv->jobrules && rid < solv->jobrules_end)
2585     {
2586       Id jidx = solv->ruletojob.elements[rid - solv->jobrules];
2587       if (fromp)
2588         *fromp = jidx;
2589       if (top)
2590         *top = solv->job.elements[jidx];
2591       if (depp)
2592         *depp = solv->job.elements[jidx + 1];
2593       if ((r->d == 0 || r->d == -1) && r->w2 == 0 && r->p == -SYSTEMSOLVABLE)
2594         {
2595           Id how = solv->job.elements[jidx];
2596           if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_INSTALL|SOLVER_SOLVABLE_NAME))
2597             return SOLVER_RULE_JOB_UNKNOWN_PACKAGE;
2598           if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES))
2599             return SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP;
2600           if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_ERASE|SOLVER_SOLVABLE_NAME))
2601             return SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM;
2602           if ((how & (SOLVER_JOBMASK|SOLVER_SELECTMASK)) == (SOLVER_ERASE|SOLVER_SOLVABLE_PROVIDES))
2603             return SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM;
2604           return SOLVER_RULE_JOB_UNSUPPORTED;
2605         }
2606       return SOLVER_RULE_JOB;
2607     }
2608   if (rid >= solv->updaterules && rid < solv->updaterules_end)
2609     {
2610       if (fromp)
2611         *fromp = solv->installed->start + (rid - solv->updaterules);
2612       return SOLVER_RULE_UPDATE;
2613     }
2614   if (rid >= solv->featurerules && rid < solv->featurerules_end)
2615     {
2616       if (fromp)
2617         *fromp = solv->installed->start + (rid - solv->featurerules);
2618       return SOLVER_RULE_FEATURE;
2619     }
2620   if (rid >= solv->duprules && rid < solv->duprules_end)
2621     {
2622       if (fromp)
2623         *fromp = -r->p;
2624       if (depp)
2625         *depp = pool->solvables[-r->p].name;
2626       return SOLVER_RULE_DISTUPGRADE;
2627     }
2628   if (rid >= solv->infarchrules && rid < solv->infarchrules_end)
2629     {
2630       if (fromp)
2631         *fromp = -r->p;
2632       if (depp)
2633         *depp = pool->solvables[-r->p].name;
2634       return SOLVER_RULE_INFARCH;
2635     }
2636   if (rid >= solv->bestrules && rid < solv->bestrules_end)
2637     {
2638       return SOLVER_RULE_BEST;
2639     }
2640   if (rid >= solv->choicerules && rid < solv->choicerules_end)
2641     {
2642       return SOLVER_RULE_CHOICE;
2643     }
2644   if (rid >= solv->learntrules)
2645     {
2646       return SOLVER_RULE_LEARNT;
2647     }
2648   return SOLVER_RULE_UNKNOWN;
2649 }
2650
2651 SolverRuleinfo
2652 solver_ruleclass(Solver *solv, Id rid)
2653 {
2654   if (rid <= 0)
2655     return SOLVER_RULE_UNKNOWN;
2656   if (rid > 0 && rid < solv->rpmrules_end)
2657     return SOLVER_RULE_RPM;
2658   if (rid >= solv->jobrules && rid < solv->jobrules_end)
2659     return SOLVER_RULE_JOB;
2660   if (rid >= solv->updaterules && rid < solv->updaterules_end)
2661     return SOLVER_RULE_UPDATE;
2662   if (rid >= solv->featurerules && rid < solv->featurerules_end)
2663     return SOLVER_RULE_FEATURE;
2664   if (rid >= solv->duprules && rid < solv->duprules_end)
2665     return SOLVER_RULE_DISTUPGRADE;
2666   if (rid >= solv->infarchrules && rid < solv->infarchrules_end)
2667     return SOLVER_RULE_INFARCH;
2668   if (rid >= solv->bestrules && rid < solv->bestrules_end)
2669     return SOLVER_RULE_BEST;
2670   if (rid >= solv->choicerules && rid < solv->choicerules_end)
2671     return SOLVER_RULE_CHOICE;
2672   if (rid >= solv->learntrules)
2673     return SOLVER_RULE_LEARNT;
2674   return SOLVER_RULE_UNKNOWN;
2675 }
2676
2677 void
2678 solver_ruleliterals(Solver *solv, Id rid, Queue *q)
2679 {
2680   Pool *pool = solv->pool;
2681   Id p, pp;
2682   Rule *r;
2683
2684   queue_empty(q);
2685   r = solv->rules + rid;
2686   FOR_RULELITERALS(p, pp, r)
2687     if (p != -SYSTEMSOLVABLE)
2688       queue_push(q, p);
2689   if (!q->count)
2690     queue_push(q, -SYSTEMSOLVABLE);     /* hmm, better to return an empty result? */
2691 }
2692
2693 int
2694 solver_rule2jobidx(Solver *solv, Id rid)
2695 {
2696   if (rid < solv->jobrules || rid >= solv->jobrules_end)
2697     return 0;
2698   return solv->ruletojob.elements[rid - solv->jobrules] + 1;
2699 }
2700
2701 /* job rule introspection */
2702 Id
2703 solver_rule2job(Solver *solv, Id rid, Id *whatp)
2704 {
2705   int idx;
2706   if (rid < solv->jobrules || rid >= solv->jobrules_end)
2707     {
2708       if (whatp)
2709         *whatp = 0;
2710       return 0;
2711     }
2712   idx = solv->ruletojob.elements[rid - solv->jobrules];
2713   if (whatp)
2714     *whatp = solv->job.elements[idx + 1];
2715   return solv->job.elements[idx];
2716 }
2717
2718 /* update/feature rule introspection */
2719 Id
2720 solver_rule2solvable(Solver *solv, Id rid)
2721 {
2722   if (rid >= solv->updaterules && rid < solv->updaterules_end)
2723     return rid - solv->updaterules;
2724   if (rid >= solv->featurerules && rid < solv->featurerules_end)
2725     return rid - solv->featurerules;
2726   return 0;
2727 }
2728
2729 static void
2730 solver_rule2rules_rec(Solver *solv, Id rid, Queue *q, Map *seen)
2731 {
2732   int i;
2733   Id rid2;
2734
2735   if (seen)
2736     MAPSET(seen, rid);
2737   for (i = solv->learnt_why.elements[rid - solv->learntrules]; (rid2 = solv->learnt_pool.elements[i]) != 0; i++)
2738     {
2739       if (seen)
2740         {
2741           if (MAPTST(seen, rid2))
2742             continue;
2743           if (rid2 >= solv->learntrules)
2744             solver_rule2rules_rec(solv, rid2, q, seen);
2745           continue;
2746         }
2747       queue_push(q, rid2);
2748     }
2749 }
2750
2751 /* learnt rule introspection */
2752 void
2753 solver_rule2rules(Solver *solv, Id rid, Queue *q, int recursive)
2754 {
2755   queue_empty(q);
2756   if (rid < solv->learntrules || rid >= solv->nrules)
2757     return;
2758   if (recursive)
2759     {
2760       Map seen;
2761       map_init(&seen, solv->nrules);
2762       solver_rule2rules_rec(solv, rid, q, &seen);
2763       map_free(&seen);
2764     }
2765   else
2766     solver_rule2rules_rec(solv, rid, q, 0);
2767 }
2768
2769
2770 /* check if the newest versions of pi still provides the dependency we're looking for */
2771 static int
2772 solver_choicerulecheck(Solver *solv, Id pi, Rule *r, Map *m)
2773 {
2774   Pool *pool = solv->pool;
2775   Rule *ur;
2776   Queue q;
2777   Id p, pp, qbuf[32];
2778   int i;
2779
2780   ur = solv->rules + solv->updaterules + (pi - pool->installed->start);
2781   if (!ur->p)
2782     ur = solv->rules + solv->featurerules + (pi - pool->installed->start);
2783   if (!ur->p)
2784     return 0;
2785   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
2786   FOR_RULELITERALS(p, pp, ur)
2787     if (p > 0)
2788       queue_push(&q, p);
2789   if (q.count > 1)
2790     policy_filter_unwanted(solv, &q, POLICY_MODE_CHOOSE);
2791   for (i = 0; i < q.count; i++)
2792     if (MAPTST(m, q.elements[i]))
2793       break;
2794   /* 1: none of the newest versions provide it */
2795   i = i == q.count ? 1 : 0;
2796   queue_free(&q);
2797   return i;
2798 }
2799
2800 static inline void
2801 queue_removeelement(Queue *q, Id el)
2802 {
2803   int i, j;
2804   for (i = 0; i < q->count; i++)
2805     if (q->elements[i] == el)
2806       break;
2807   if (i < q->count)
2808     {
2809       for (j = i++; i < q->count; i++)
2810         if (q->elements[i] != el)
2811           q->elements[j++] = q->elements[i];
2812       queue_truncate(q, j);
2813     }
2814 }
2815
2816 void
2817 solver_addchoicerules(Solver *solv)
2818 {
2819   Pool *pool = solv->pool;
2820   Map m, mneg;
2821   Rule *r;
2822   Queue q, qi;
2823   int i, j, rid, havechoice;
2824   Id p, d, pp;
2825   Id p2, pp2;
2826   Solvable *s, *s2;
2827   Id lastaddedp, lastaddedd;
2828   int lastaddedcnt;
2829   unsigned int now;
2830
2831   solv->choicerules = solv->nrules;
2832   if (!pool->installed)
2833     {
2834       solv->choicerules_end = solv->nrules;
2835       return;
2836     }
2837   now = solv_timems(0);
2838   solv->choicerules_ref = solv_calloc(solv->rpmrules_end, sizeof(Id));
2839   queue_init(&q);
2840   queue_init(&qi);
2841   map_init(&m, pool->nsolvables);
2842   map_init(&mneg, pool->nsolvables);
2843   /* set up negative assertion map from infarch and dup rules */
2844   for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++)
2845     if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
2846       MAPSET(&mneg, -r->p);
2847   for (rid = solv->duprules, r = solv->rules + rid; rid < solv->duprules_end; rid++, r++)
2848     if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
2849       MAPSET(&mneg, -r->p);
2850   lastaddedp = 0;
2851   lastaddedd = 0;
2852   lastaddedcnt = 0;
2853   for (rid = 1; rid < solv->rpmrules_end ; rid++)
2854     {
2855       r = solv->rules + rid;
2856       if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
2857         continue;       /* only look at requires rules */
2858       /* solver_printrule(solv, SOLV_DEBUG_RESULT, r); */
2859       queue_empty(&q);
2860       queue_empty(&qi);
2861       havechoice = 0;
2862       FOR_RULELITERALS(p, pp, r)
2863         {
2864           if (p < 0)
2865             continue;
2866           s = pool->solvables + p;
2867           if (!s->repo)
2868             continue;
2869           if (s->repo == pool->installed)
2870             {
2871               queue_push(&q, p);
2872               continue;
2873             }
2874           /* check if this package is "blocked" by a installed package */
2875           s2 = 0;
2876           FOR_PROVIDES(p2, pp2, s->name)
2877             {
2878               s2 = pool->solvables + p2;
2879               if (s2->repo != pool->installed)
2880                 continue;
2881               if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
2882                 continue;
2883               if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, s2))
2884                 continue;
2885               break;
2886             }
2887           if (p2)
2888             {
2889               /* found installed package p2 that we can update to p */
2890               if (MAPTST(&mneg, p))
2891                 continue;
2892               if (policy_is_illegal(solv, s2, s, 0))
2893                 continue;
2894 #if 0
2895               if (solver_choicerulecheck(solv, p2, r, &m))
2896                 continue;
2897               queue_push(&qi, p2);
2898 #else
2899               queue_push2(&qi, p2, p);
2900 #endif
2901               queue_push(&q, p);
2902               continue;
2903             }
2904           if (s->obsoletes)
2905             {
2906               Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
2907               s2 = 0;
2908               while ((obs = *obsp++) != 0)
2909                 {
2910                   FOR_PROVIDES(p2, pp2, obs)
2911                     {
2912                       s2 = pool->solvables + p2;
2913                       if (s2->repo != pool->installed)
2914                         continue;
2915                       if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
2916                         continue;
2917                       if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
2918                         continue;
2919                       break;
2920                     }
2921                   if (p2)
2922                     break;
2923                 }
2924               if (obs)
2925                 {
2926                   /* found installed package p2 that we can update to p */
2927                   if (MAPTST(&mneg, p))
2928                     continue;
2929                   if (policy_is_illegal(solv, s2, s, 0))
2930                     continue;
2931 #if 0
2932                   if (solver_choicerulecheck(solv, p2, r, &m))
2933                     continue;
2934                   queue_push(&qi, p2);
2935 #else
2936                   queue_push2(&qi, p2, p);
2937 #endif
2938                   queue_push(&q, p);
2939                   continue;
2940                 }
2941             }
2942           /* package p is independent of the installed ones */
2943           havechoice = 1;
2944         }
2945       if (!havechoice || !q.count || !qi.count)
2946         continue;       /* no choice */
2947
2948       FOR_RULELITERALS(p, pp, r)
2949         if (p > 0)
2950           MAPSET(&m, p);
2951
2952       /* do extra checking */
2953       for (i = j = 0; i < qi.count; i += 2)
2954         {
2955           p2 = qi.elements[i];
2956           if (!p2)
2957             continue;
2958           if (solver_choicerulecheck(solv, p2, r, &m))
2959             {
2960               /* oops, remove element p from q */
2961               queue_removeelement(&q, qi.elements[i + 1]);
2962               continue;
2963             }
2964           qi.elements[j++] = p2;
2965         }
2966       queue_truncate(&qi, j);
2967       if (!q.count || !qi.count)
2968         {
2969           FOR_RULELITERALS(p, pp, r)
2970             if (p > 0)
2971               MAPCLR(&m, p);
2972           continue;
2973         }
2974
2975
2976       /* now check the update rules of the installed package.
2977        * if all packages of the update rules are contained in
2978        * the dependency rules, there's no need to set up the choice rule */
2979       for (i = 0; i < qi.count; i++)
2980         {
2981           Rule *ur;
2982           if (!qi.elements[i])
2983             continue;
2984           ur = solv->rules + solv->updaterules + (qi.elements[i] - pool->installed->start);
2985           if (!ur->p)
2986             ur = solv->rules + solv->featurerules + (qi.elements[i] - pool->installed->start);
2987           if (!ur->p)
2988             continue;
2989           FOR_RULELITERALS(p, pp, ur)
2990             if (!MAPTST(&m, p))
2991               break;
2992           if (p)
2993             break;
2994           for (j = i + 1; j < qi.count; j++)
2995             if (qi.elements[i] == qi.elements[j])
2996               qi.elements[j] = 0;
2997         }
2998       /* empty map again */
2999       FOR_RULELITERALS(p, pp, r)
3000         if (p > 0)
3001           MAPCLR(&m, p);
3002       if (i == qi.count)
3003         {
3004 #if 0
3005           printf("skipping choice ");
3006           solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + rid);
3007 #endif
3008           continue;
3009         }
3010
3011       /* don't add identical rules */
3012       if (lastaddedp == r->p && lastaddedcnt == q.count)
3013         {
3014           for (i = 0; i < q.count; i++)
3015             if (q.elements[i] != pool->whatprovidesdata[lastaddedd + i])
3016               break;
3017           if (i == q.count)
3018             continue;   /* already added that one */
3019         }
3020       d = q.count ? pool_queuetowhatprovides(pool, &q) : 0;
3021
3022       lastaddedp = r->p;
3023       lastaddedd = d;
3024       lastaddedcnt = q.count;
3025
3026       solver_addrule(solv, r->p, d);
3027       queue_push(&solv->weakruleq, solv->nrules - 1);
3028       solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid;
3029 #if 0
3030       printf("OLD ");
3031       solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + rid);
3032       printf("WEAK CHOICE ");
3033       solver_printrule(solv, SOLV_DEBUG_RESULT, solv->rules + solv->nrules - 1);
3034 #endif
3035     }
3036   queue_free(&q);
3037   queue_free(&qi);
3038   map_free(&m);
3039   map_free(&mneg);
3040   solv->choicerules_end = solv->nrules;
3041   /* shrink choicerules_ref */
3042   solv->choicerules_ref = solv_realloc2(solv->choicerules_ref, solv->choicerules_end - solv->choicerules, sizeof(Id));
3043   POOL_DEBUG(SOLV_DEBUG_STATS, "choice rule creation took %d ms\n", solv_timems(now));
3044 }
3045
3046 /* called when a choice rule is disabled by analyze_unsolvable. We also
3047  * have to disable all other choice rules so that the best packages get
3048  * picked */
3049 void
3050 solver_disablechoicerules(Solver *solv, Rule *r)
3051 {
3052   Id rid, p, pp;
3053   Pool *pool = solv->pool;
3054   Map m;
3055   Rule *or;
3056
3057   or = solv->rules + solv->choicerules_ref[(r - solv->rules) - solv->choicerules];
3058   map_init(&m, pool->nsolvables);
3059   FOR_RULELITERALS(p, pp, or)
3060     if (p > 0)
3061       MAPSET(&m, p);
3062   FOR_RULELITERALS(p, pp, r)
3063     if (p > 0)
3064       MAPCLR(&m, p);
3065   for (rid = solv->choicerules; rid < solv->choicerules_end; rid++)
3066     {
3067       r = solv->rules + rid;
3068       if (r->d < 0)
3069         continue;
3070       or = solv->rules + solv->choicerules_ref[(r - solv->rules) - solv->choicerules];
3071       FOR_RULELITERALS(p, pp, or)
3072         if (p > 0 && MAPTST(&m, p))
3073           break;
3074       if (p)
3075         solver_disablerule(solv, r);
3076     }
3077 }
3078
3079 static void
3080 prune_to_update_targets(Solver *solv, Id *cp, Queue *q)
3081 {
3082   int i, j;
3083   Id p, *cp2;
3084   for (i = j = 0; i < q->count; i++)
3085     {
3086       p = q->elements[i];
3087       for (cp2 = cp; *cp2; cp2++)
3088         if (*cp2 == p)
3089           {
3090             q->elements[j++] = p;
3091             break;
3092           }
3093     }
3094   queue_truncate(q, j);
3095 }
3096
3097 static void
3098 prune_to_dup_packages(Solver *solv, Id p, Queue *q)
3099 {
3100   int i, j;
3101   for (i = j = 0; i < q->count; i++)
3102     {
3103       Id p = q->elements[i];
3104       if (MAPTST(&solv->dupmap, p))
3105         q->elements[j++] = p;
3106     }
3107   queue_truncate(q, j);
3108 }
3109
3110 void
3111 solver_addbestrules(Solver *solv, int havebestinstalljobs)
3112 {
3113   Pool *pool = solv->pool;
3114   Id p;
3115   Solvable *s;
3116   Repo *installed = solv->installed;
3117   Queue q, q2;
3118   Rule *r;
3119   Queue r2pkg;
3120   int i, oldcnt;
3121
3122   solv->bestrules = solv->nrules;
3123   if (!installed)
3124     {
3125       solv->bestrules_end = solv->nrules;
3126       return;
3127     }
3128   queue_init(&q);
3129   queue_init(&q2);
3130   queue_init(&r2pkg);
3131
3132   if (havebestinstalljobs)
3133     {
3134       for (i = 0; i < solv->job.count; i += 2)
3135         {
3136           if ((solv->job.elements[i] & (SOLVER_JOBMASK | SOLVER_FORCEBEST)) == (SOLVER_INSTALL | SOLVER_FORCEBEST))
3137             {
3138               int j;
3139               Id p2, pp2;
3140               for (j = 0; j < solv->ruletojob.count; j++)
3141                 if (solv->ruletojob.elements[j] == i)
3142                   break;
3143               if (j == solv->ruletojob.count)
3144                 continue;
3145               r = solv->rules + solv->jobrules + j;
3146               queue_empty(&q);
3147               FOR_RULELITERALS(p2, pp2, r)
3148                 if (p2 > 0)
3149                   queue_push(&q, p2);
3150               if (!q.count)
3151                 continue;       /* orphaned */
3152               /* select best packages, just look at prio and version */
3153               oldcnt = q.count;
3154               policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
3155               if (q.count == oldcnt)
3156                 continue;       /* nothing filtered */
3157               p2 = queue_shift(&q);
3158               solver_addrule(solv, p2, q.count ? pool_queuetowhatprovides(pool, &q) : 0);
3159               queue_push(&r2pkg, -(solv->jobrules + j));
3160             }
3161         }
3162     }
3163
3164   if (solv->bestupdatemap_all || solv->bestupdatemap.size)
3165     {
3166       FOR_REPO_SOLVABLES(installed, p, s)
3167         {
3168           Id d, p2, pp2;
3169           if (!solv->updatemap_all && (!solv->updatemap.size || !MAPTST(&solv->updatemap, p - installed->start)))
3170             continue;
3171           if (!solv->bestupdatemap_all && (!solv->bestupdatemap.size || !MAPTST(&solv->bestupdatemap, p - installed->start)))
3172             continue;
3173           queue_empty(&q);
3174           if (solv->bestobeypolicy)
3175             r = solv->rules + solv->updaterules + (p - installed->start);
3176           else
3177             {
3178               r = solv->rules + solv->featurerules + (p - installed->start);
3179               if (!r->p)        /* identical to update rule? */
3180                 r = solv->rules + solv->updaterules + (p - installed->start);
3181             }
3182           if (solv->specialupdaters && (d = solv->specialupdaters[p - installed->start]) != 0 && r == solv->rules + solv->updaterules + (p - installed->start))
3183             {
3184               /* need to check specialupdaters */
3185               if (r->p == p)    /* be careful with the dup case */
3186                 queue_push(&q, p);
3187               while ((p2 = pool->whatprovidesdata[d++]) != 0)
3188                 queue_push(&q, p2);
3189             }
3190           else
3191             {
3192               FOR_RULELITERALS(p2, pp2, r)
3193                 if (p2 > 0)
3194                   queue_push(&q, p2);
3195             }
3196           if (solv->update_targets && solv->update_targets->elements[p - installed->start])
3197             prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q);
3198           if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
3199             prune_to_dup_packages(solv, p, &q);
3200           /* select best packages, just look at prio and version */
3201           policy_filter_unwanted(solv, &q, POLICY_MODE_RECOMMEND);
3202           if (!q.count)
3203             continue;   /* orphaned */
3204           if (solv->bestobeypolicy)
3205             {
3206               /* also filter the best of the feature rule packages and add them */
3207               r = solv->rules + solv->featurerules + (p - installed->start);
3208               if (r->p)
3209                 {
3210                   int j;
3211                   queue_empty(&q2);
3212                   FOR_RULELITERALS(p2, pp2, r)
3213                     if (p2 > 0)
3214                       queue_push(&q2, p2);
3215                   if (solv->update_targets && solv->update_targets->elements[p - installed->start])
3216                     prune_to_update_targets(solv, solv->update_targets->elements + solv->update_targets->elements[p - installed->start], &q2);
3217                   if (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
3218                     prune_to_dup_packages(solv, p, &q2);
3219                   policy_filter_unwanted(solv, &q2, POLICY_MODE_RECOMMEND);
3220                   for (j = 0; j < q2.count; j++)
3221                     queue_pushunique(&q, q2.elements[j]);
3222                 }
3223             }
3224           p2 = queue_shift(&q);
3225           solver_addrule(solv, p2, q.count ? pool_queuetowhatprovides(pool, &q) : 0);
3226           queue_push(&r2pkg, p);
3227         }
3228     }
3229   if (r2pkg.count)
3230     solv->bestrules_pkg = solv_memdup2(r2pkg.elements, r2pkg.count, sizeof(Id));
3231   solv->bestrules_end = solv->nrules;
3232   queue_free(&q);
3233   queue_free(&q2);
3234   queue_free(&r2pkg);
3235 }
3236
3237 #undef CLEANDEPSDEBUG
3238
3239 /*
3240  * This functions collects all packages that are looked at
3241  * when a dependency is checked. We need it to "pin" installed
3242  * packages when removing a supplemented package in createcleandepsmap.
3243  * Here's an not uncommon example:
3244  *   A contains "Supplements: packageand(B, C)"
3245  *   B contains "Requires: A"
3246  * Now if we remove C, the supplements is no longer true,
3247  * thus we also remove A. Without the dep_pkgcheck function, we
3248  * would now also remove B, but this is wrong, as adding back
3249  * C doesn't make the supplements true again. Thus we "pin" B
3250  * when we remove A.
3251  * There's probably a better way to do this, but I haven't come
3252  * up with it yet ;)
3253  */
3254 static inline void
3255 dep_pkgcheck(Solver *solv, Id dep, Map *m, Queue *q)
3256 {
3257   Pool *pool = solv->pool;
3258   Id p, pp;
3259
3260   if (ISRELDEP(dep))
3261     {
3262       Reldep *rd = GETRELDEP(pool, dep);
3263       if (rd->flags >= 8)
3264         {
3265           if (rd->flags == REL_AND)
3266             {
3267               dep_pkgcheck(solv, rd->name, m, q);
3268               dep_pkgcheck(solv, rd->evr, m, q);
3269               return;
3270             }
3271           if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
3272             return;
3273           if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_INSTALLED)
3274             return;
3275         }
3276     }
3277   FOR_PROVIDES(p, pp, dep)
3278     if (!m || MAPTST(m, p))
3279       queue_push(q, p);
3280 }
3281
3282 static int
3283 check_xsupp(Solver *solv, Queue *depq, Id dep)
3284 {
3285   Pool *pool = solv->pool;
3286   Id p, pp;
3287
3288   if (ISRELDEP(dep))
3289     {
3290       Reldep *rd = GETRELDEP(pool, dep);
3291       if (rd->flags >= 8)
3292         {
3293           if (rd->flags == REL_AND)
3294             {
3295               if (!check_xsupp(solv, depq, rd->name))
3296                 return 0;
3297               return check_xsupp(solv, depq, rd->evr);
3298             }
3299           if (rd->flags == REL_OR)
3300             {
3301               if (check_xsupp(solv, depq, rd->name))
3302                 return 1;
3303               return check_xsupp(solv, depq, rd->evr);
3304             }
3305           if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
3306             return solver_splitprovides(solv, rd->evr);
3307           if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_INSTALLED)
3308             return solver_dep_installed(solv, rd->evr);
3309         }
3310       if (depq && rd->flags == REL_NAMESPACE)
3311         {
3312           int i;
3313           for (i = 0; i < depq->count; i++)
3314             if (depq->elements[i] == dep || depq->elements[i] == rd->name)
3315              return 1;
3316         }
3317     }
3318   FOR_PROVIDES(p, pp, dep)
3319     if (p == SYSTEMSOLVABLE || pool->solvables[p].repo == solv->installed)
3320       return 1;
3321   return 0;
3322 }
3323
3324 static inline int
3325 queue_contains(Queue *q, Id id)
3326 {
3327   int i;
3328   for (i = 0; i < q->count; i++)
3329     if (q->elements[i] == id)
3330       return 1;
3331   return 0;
3332 }
3333
3334
3335 /*
3336  * Find all installed packages that are no longer
3337  * needed regarding the current solver job.
3338  *
3339  * The algorithm is:
3340  * - remove pass: remove all packages that could have
3341  *   been dragged in by the obsoleted packages.
3342  *   i.e. if package A is obsolete and contains "Requires: B",
3343  *   also remove B, as installing A will have pulled in B.
3344  *   after this pass, we have a set of still installed packages
3345  *   with broken dependencies.
3346  * - add back pass:
3347  *   now add back all packages that the still installed packages
3348  *   require.
3349  *
3350  * The cleandeps packages are the packages removed in the first
3351  * pass and not added back in the second pass.
3352  *
3353  * If we search for unneeded packages (unneeded is true), we
3354  * simply remove all packages except the userinstalled ones in
3355  * the first pass.
3356  */
3357 static void
3358 solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
3359 {
3360   Pool *pool = solv->pool;
3361   Repo *installed = solv->installed;
3362   Queue *job = &solv->job;
3363   Map userinstalled;
3364   Map im;
3365   Map installedm;
3366   Rule *r;
3367   Id rid, how, what, select;
3368   Id p, pp, ip, jp;
3369   Id req, *reqp, sup, *supp;
3370   Solvable *s;
3371   Queue iq, iqcopy, xsuppq;
3372   int i;
3373
3374   map_empty(cleandepsmap);
3375   if (!installed || installed->end == installed->start)
3376     return;
3377   map_init(&userinstalled, installed->end - installed->start);
3378   map_init(&im, pool->nsolvables);
3379   map_init(&installedm, pool->nsolvables);
3380   queue_init(&iq);
3381   queue_init(&xsuppq);
3382
3383   for (i = 0; i < job->count; i += 2)
3384     {
3385       how = job->elements[i];
3386       if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
3387         {
3388           what = job->elements[i + 1];
3389           select = how & SOLVER_SELECTMASK;
3390           if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
3391             FOR_REPO_SOLVABLES(installed, p, s)
3392               MAPSET(&userinstalled, p - installed->start);
3393           FOR_JOB_SELECT(p, pp, select, what)
3394             if (pool->solvables[p].repo == installed)
3395               MAPSET(&userinstalled, p - installed->start);
3396         }
3397       if ((how & (SOLVER_JOBMASK | SOLVER_SELECTMASK)) == (SOLVER_ERASE | SOLVER_SOLVABLE_PROVIDES))
3398         {
3399           what = job->elements[i + 1];
3400           if (ISRELDEP(what))
3401             {
3402               Reldep *rd = GETRELDEP(pool, what);
3403               if (rd->flags != REL_NAMESPACE)
3404                 continue;
3405               if (rd->evr == 0)
3406                 {
3407                   queue_pushunique(&iq, rd->name);
3408                   continue;
3409                 }
3410               FOR_PROVIDES(p, pp, what)
3411                 if (p)
3412                   break;
3413               if (p)
3414                 continue;
3415               queue_pushunique(&iq, what);
3416             }
3417         }
3418     }
3419
3420   /* have special namespace cleandeps erases */
3421   if (iq.count)
3422     {
3423       for (ip = solv->installed->start; ip < solv->installed->end; ip++)
3424         {
3425           s = pool->solvables + ip;
3426           if (s->repo != installed)
3427             continue;
3428           if (!s->supplements)
3429             continue;
3430           supp = s->repo->idarraydata + s->supplements;
3431           while ((sup = *supp++) != 0)
3432             if (check_xsupp(solv, &iq, sup) && !check_xsupp(solv, 0, sup))
3433               {
3434 #ifdef CLEANDEPSDEBUG
3435                 printf("xsupp %s from %s\n", pool_dep2str(pool, sup), pool_solvid2str(pool, ip));
3436 #endif
3437                 queue_pushunique(&xsuppq, sup);
3438               }
3439         }
3440       queue_empty(&iq);
3441     }
3442
3443   /* also add visible patterns to userinstalled for openSUSE */
3444   if (1)
3445     {
3446       Dataiterator di;
3447       dataiterator_init(&di, pool, 0, 0, SOLVABLE_ISVISIBLE, 0, 0);
3448       while (dataiterator_step(&di))
3449         {
3450           Id *dp;
3451           if (di.solvid <= 0)
3452             continue;
3453           s = pool->solvables + di.solvid;
3454           if (!s->repo || !s->requires)
3455             continue;
3456           if (s->repo != installed && !pool_installable(pool, s))
3457             continue;
3458           if (strncmp(pool_id2str(pool, s->name), "pattern:", 8) != 0)
3459             continue;
3460           dp = s->repo->idarraydata + s->requires;
3461           for (dp = s->repo->idarraydata + s->requires; *dp; dp++)
3462             FOR_PROVIDES(p, pp, *dp)
3463               if (pool->solvables[p].repo == installed)
3464                 {
3465                   if (strncmp(pool_id2str(pool, pool->solvables[p].name), "pattern", 7) != 0)
3466                     continue;
3467                   MAPSET(&userinstalled, p - installed->start);
3468                 }
3469         }
3470       dataiterator_free(&di);
3471     }
3472   if (1)
3473     {
3474       /* all products and their buddies are userinstalled */
3475       for (p = installed->start; p < installed->end; p++)
3476         {
3477           Solvable *s = pool->solvables + p;
3478           if (s->repo != installed)
3479             continue;
3480           if (!strncmp("product:", pool_id2str(pool, s->name), 8))
3481             {
3482               MAPSET(&userinstalled, p - installed->start);
3483               if (pool->nscallback)
3484                 {
3485                   Id buddy = pool->nscallback(pool, pool->nscallbackdata, NAMESPACE_PRODUCTBUDDY, p);
3486                   if (buddy >= installed->start && buddy < installed->end && pool->solvables[buddy].repo == installed)
3487                     MAPSET(&userinstalled, buddy - installed->start);
3488                 }
3489             }
3490         }
3491     }
3492
3493   /* add all positive elements (e.g. locks) to "userinstalled" */
3494   for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
3495     {
3496       r = solv->rules + rid;
3497       if (r->d < 0)
3498         continue;
3499       i = solv->ruletojob.elements[rid - solv->jobrules];
3500       if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
3501         continue;
3502       FOR_RULELITERALS(p, jp, r)
3503         if (p > 0 && pool->solvables[p].repo == installed)
3504           MAPSET(&userinstalled, p - installed->start);
3505     }
3506
3507   /* add all cleandeps candidates to iq */
3508   for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
3509     {
3510       r = solv->rules + rid;
3511       if (r->d < 0)                             /* disabled? */
3512         continue;
3513       if (r->d == 0 && r->p < 0 && r->w2 == 0)  /* negative assertion (erase job)? */
3514         {
3515           p = -r->p;
3516           if (pool->solvables[p].repo != installed)
3517             continue;
3518           MAPCLR(&userinstalled, p - installed->start);
3519           if (unneeded)
3520             continue;
3521           i = solv->ruletojob.elements[rid - solv->jobrules];
3522           how = job->elements[i];
3523           if ((how & (SOLVER_JOBMASK|SOLVER_CLEANDEPS)) == (SOLVER_ERASE|SOLVER_CLEANDEPS))
3524             queue_push(&iq, p);
3525         }
3526       else if (r->p > 0)                        /* install job */
3527         {
3528           if (unneeded)
3529             continue;
3530           i = solv->ruletojob.elements[rid - solv->jobrules];
3531           if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
3532             {
3533               /* check if the literals all obsolete some installed package */
3534               Map om;
3535               int iqstart;
3536
3537               /* just one installed literal */
3538               if (r->d == 0 && r->w2 == 0 && pool->solvables[r->p].repo == installed)
3539                 continue;
3540               /* multiversion is bad */
3541               if (solv->multiversion.size && !solv->keepexplicitobsoletes)
3542                 {
3543                   FOR_RULELITERALS(p, jp, r)
3544                     if (MAPTST(&solv->multiversion, p))
3545                       break;
3546                   if (p)
3547                     continue;
3548                 }
3549
3550               om.size = 0;
3551               iqstart = iq.count;
3552               FOR_RULELITERALS(p, jp, r)
3553                 {
3554                   if (p < 0)
3555                     {
3556                       queue_truncate(&iq, iqstart);     /* abort */
3557                       break;
3558                     }
3559                   if (pool->solvables[p].repo == installed)
3560                     {
3561                       if (iq.count == iqstart)
3562                         queue_push(&iq, p);
3563                       else
3564                         {
3565                           for (i = iqstart; i < iq.count; i++)
3566                             if (iq.elements[i] == p)
3567                               break;
3568                           queue_truncate(&iq, iqstart);
3569                           if (i < iq.count)
3570                             queue_push(&iq, p);
3571                         }
3572                     }
3573                   else
3574                     intersect_obsoletes(solv, p, &iq, iqstart, &om);
3575                   if (iq.count == iqstart)
3576                     break;
3577                 }
3578               if (om.size)
3579                 map_free(&om);
3580             }
3581         }
3582     }
3583   queue_init_clone(&iqcopy, &iq);
3584
3585   if (!unneeded)
3586     {
3587       if (solv->cleandeps_updatepkgs)
3588         for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
3589           queue_push(&iq, solv->cleandeps_updatepkgs->elements[i]);
3590     }
3591
3592   if (unneeded)
3593     queue_empty(&iq);   /* just in case... */
3594
3595   /* clear userinstalled bit for the packages we really want to delete/update */
3596   for (i = 0; i < iq.count; i++)
3597     {
3598       p = iq.elements[i];
3599       if (pool->solvables[p].repo != installed)
3600         continue;
3601       MAPCLR(&userinstalled, p - installed->start);
3602     }
3603
3604   for (p = installed->start; p < installed->end; p++)
3605     {
3606       if (pool->solvables[p].repo != installed)
3607         continue;
3608       MAPSET(&installedm, p);
3609       if (unneeded && !MAPTST(&userinstalled, p - installed->start))
3610         continue;
3611       MAPSET(&im, p);
3612     }
3613   MAPSET(&installedm, SYSTEMSOLVABLE);
3614   MAPSET(&im, SYSTEMSOLVABLE);
3615
3616 #ifdef CLEANDEPSDEBUG
3617   printf("REMOVE PASS\n");
3618 #endif
3619
3620   for (;;)
3621     {
3622       if (!iq.count)
3623         {
3624           if (unneeded)
3625             break;
3626           /* supplements pass */
3627           for (ip = installed->start; ip < installed->end; ip++)
3628             {
3629               if (!MAPTST(&installedm, ip))
3630                 continue;
3631               s = pool->solvables + ip;
3632               if (!s->supplements)
3633                 continue;
3634               if (!MAPTST(&im, ip))
3635                 continue;
3636               if (MAPTST(&userinstalled, ip - installed->start))
3637                 continue;
3638               supp = s->repo->idarraydata + s->supplements;
3639               while ((sup = *supp++) != 0)
3640                 if (dep_possible(solv, sup, &im))
3641                   break;
3642               if (!sup)
3643                 {
3644                   supp = s->repo->idarraydata + s->supplements;
3645                   while ((sup = *supp++) != 0)
3646                     if (dep_possible(solv, sup, &installedm) || (xsuppq.count && queue_contains(&xsuppq, sup)))
3647                       {
3648                         /* no longer supplemented, also erase */
3649                         int iqcount = iq.count;
3650                         /* pin packages, see comment above dep_pkgcheck */
3651                         dep_pkgcheck(solv, sup, &im, &iq);
3652                         for (i = iqcount; i < iq.count; i++)
3653                           {
3654                             Id pqp = iq.elements[i];
3655                             if (pool->solvables[pqp].repo == installed)
3656                               MAPSET(&userinstalled, pqp - installed->start);
3657                           }
3658                         queue_truncate(&iq, iqcount);
3659 #ifdef CLEANDEPSDEBUG
3660                         printf("%s supplemented [%s]\n", pool_solvid2str(pool, ip), pool_dep2str(pool, sup));
3661 #endif
3662                         queue_push(&iq, ip);
3663                       }
3664                 }
3665             }
3666           if (!iq.count)
3667             break;      /* no supplementing package found, we're done */
3668         }
3669       ip = queue_shift(&iq);
3670       s = pool->solvables + ip;
3671       if (!MAPTST(&im, ip))
3672         continue;
3673       if (!MAPTST(&installedm, ip))
3674         continue;
3675       if (s->repo == installed && MAPTST(&userinstalled, ip - installed->start))
3676         continue;
3677       MAPCLR(&im, ip);
3678 #ifdef CLEANDEPSDEBUG
3679       printf("removing %s\n", pool_solvable2str(pool, s));
3680 #endif
3681       if (s->requires)
3682         {
3683           reqp = s->repo->idarraydata + s->requires;
3684           while ((req = *reqp++) != 0)
3685             {
3686               if (req == SOLVABLE_PREREQMARKER)
3687                 continue;
3688 #if 0
3689               /* count number of installed packages that match */
3690               count = 0;
3691               FOR_PROVIDES(p, pp, req)
3692                 if (MAPTST(&installedm, p))
3693                   count++;
3694               if (count > 1)
3695                 continue;
3696 #endif
3697               FOR_PROVIDES(p, pp, req)
3698                 {
3699                   if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
3700                     {
3701 #ifdef CLEANDEPSDEBUG
3702                       printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
3703 #endif
3704                       queue_push(&iq, p);
3705                     }
3706                 }
3707             }
3708         }
3709       if (s->recommends)
3710         {
3711           reqp = s->repo->idarraydata + s->recommends;
3712           while ((req = *reqp++) != 0)
3713             {
3714 #if 0
3715               count = 0;
3716               FOR_PROVIDES(p, pp, req)
3717                 if (MAPTST(&installedm, p))
3718                   count++;
3719               if (count > 1)
3720                 continue;
3721 #endif
3722               FOR_PROVIDES(p, pp, req)
3723                 {
3724                   if (p != SYSTEMSOLVABLE && MAPTST(&im, p))
3725                     {
3726 #ifdef CLEANDEPSDEBUG
3727                       printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
3728 #endif
3729                       queue_push(&iq, p);
3730                     }
3731                 }
3732             }
3733         }
3734     }
3735
3736   /* turn userinstalled into remove set for pruning */
3737   map_empty(&userinstalled);
3738   for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
3739     {
3740       r = solv->rules + rid;
3741       if (r->p >= 0 || r->d != 0 || r->w2 != 0)
3742         continue;       /* disabled or not erase */
3743       p = -r->p;
3744       MAPCLR(&im, p);
3745       if (pool->solvables[p].repo == installed)
3746         MAPSET(&userinstalled, p - installed->start);
3747     }
3748   if (!unneeded && solv->cleandeps_updatepkgs)
3749     {
3750       for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
3751         {
3752           p = solv->cleandeps_updatepkgs->elements[i];
3753           if (pool->solvables[p].repo == installed)
3754             MAPSET(&userinstalled, p - installed->start);
3755         }
3756     }
3757   MAPSET(&im, SYSTEMSOLVABLE);  /* in case we cleared it above */
3758   for (p = installed->start; p < installed->end; p++)
3759     if (MAPTST(&im, p))
3760       queue_push(&iq, p);
3761   for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
3762     {
3763       r = solv->rules + rid;
3764       if (r->d < 0)
3765         continue;
3766       FOR_RULELITERALS(p, jp, r)
3767         if (p > 0)
3768           queue_push(&iq, p);
3769     }
3770   /* also put directly addressed packages on the install queue
3771    * so we can mark patterns as installed */
3772   for (i = 0; i < job->count; i += 2)
3773     {
3774       how = job->elements[i];
3775       if ((how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
3776         {
3777           what = job->elements[i + 1];
3778           select = how & SOLVER_SELECTMASK;
3779           if (select == SOLVER_SOLVABLE && pool->solvables[what].repo != installed)
3780             queue_push(&iq, what);
3781         }
3782     }
3783
3784 #ifdef CLEANDEPSDEBUG
3785   printf("ADDBACK PASS\n");
3786 #endif
3787   for (;;)
3788     {
3789       if (!iq.count)
3790         {
3791           /* supplements pass */
3792           for (ip = installed->start; ip < installed->end; ip++)
3793             {
3794               if (!MAPTST(&installedm, ip))
3795                 continue;
3796               if (MAPTST(&userinstalled, ip - installed->start))
3797                 continue;
3798               s = pool->solvables + ip;
3799               if (!s->supplements)
3800                 continue;
3801               if (MAPTST(&im, ip))
3802                 continue;
3803               supp = s->repo->idarraydata + s->supplements;
3804               while ((sup = *supp++) != 0)
3805                 if (dep_possible(solv, sup, &im))
3806                   break;
3807               if (sup)
3808                 {
3809 #ifdef CLEANDEPSDEBUG
3810                   printf("%s supplemented\n", pool_solvid2str(pool, ip));
3811 #endif
3812                   MAPSET(&im, ip);
3813                   queue_push(&iq, ip);
3814                 }
3815             }
3816           if (!iq.count)
3817             break;
3818         }
3819       ip = queue_shift(&iq);
3820       s = pool->solvables + ip;
3821 #ifdef CLEANDEPSDEBUG
3822       printf("adding back %s\n", pool_solvable2str(pool, s));
3823 #endif
3824       if (s->requires)
3825         {
3826           reqp = s->repo->idarraydata + s->requires;
3827           while ((req = *reqp++) != 0)
3828             {
3829               FOR_PROVIDES(p, pp, req)
3830                 if (MAPTST(&im, p))
3831                   break;
3832               if (p)
3833                 continue;
3834               FOR_PROVIDES(p, pp, req)
3835                 {
3836                   if (!MAPTST(&im, p) && MAPTST(&installedm, p))
3837                     {
3838                       if (p == ip)
3839                         continue;
3840                       if (MAPTST(&userinstalled, p - installed->start))
3841                         continue;
3842 #ifdef CLEANDEPSDEBUG
3843                       printf("%s requires %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
3844 #endif
3845                       MAPSET(&im, p);
3846                       queue_push(&iq, p);
3847                     }
3848                 }
3849             }
3850         }
3851       if (s->recommends)
3852         {
3853           reqp = s->repo->idarraydata + s->recommends;
3854           while ((req = *reqp++) != 0)
3855             {
3856               FOR_PROVIDES(p, pp, req)
3857                 if (MAPTST(&im, p))
3858                   break;
3859               if (p)
3860                 continue;
3861               FOR_PROVIDES(p, pp, req)
3862                 {
3863                   if (!MAPTST(&im, p) && MAPTST(&installedm, p))
3864                     {
3865                       if (p == ip)
3866                         continue;
3867                       if (MAPTST(&userinstalled, p - installed->start))
3868                         continue;
3869 #ifdef CLEANDEPSDEBUG
3870                       printf("%s recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
3871 #endif
3872                       MAPSET(&im, p);
3873                       queue_push(&iq, p);
3874                     }
3875                 }
3876             }
3877         }
3878     }
3879
3880   queue_free(&iq);
3881   /* make sure the updatepkgs and mistakes are not in the cleandeps map */
3882   if (solv->cleandeps_updatepkgs)
3883     for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
3884       MAPSET(&im, solv->cleandeps_updatepkgs->elements[i]);
3885   if (solv->cleandeps_mistakes)
3886     for (i = 0; i < solv->cleandeps_mistakes->count; i++)
3887       MAPSET(&im, solv->cleandeps_mistakes->elements[i]);
3888   /* also remove original iq packages */
3889   for (i = 0; i < iqcopy.count; i++)
3890     MAPSET(&im, iqcopy.elements[i]);
3891   queue_free(&iqcopy);
3892   for (p = installed->start; p < installed->end; p++)
3893     {
3894       if (pool->solvables[p].repo != installed)
3895         continue;
3896       if (!MAPTST(&im, p))
3897         MAPSET(cleandepsmap, p - installed->start);
3898     }
3899   map_free(&im);
3900   map_free(&installedm);
3901   map_free(&userinstalled);
3902   queue_free(&xsuppq);
3903 #ifdef CLEANDEPSDEBUG
3904   printf("=== final cleandeps map:\n");
3905   for (p = installed->start; p < installed->end; p++)
3906     if (MAPTST(cleandepsmap, p - installed->start))
3907       printf("  - %s\n", pool_solvid2str(pool, p));
3908 #endif
3909 }
3910
3911
3912 struct trj_data {
3913   Queue *edges;
3914   Id *low;
3915   Id idx;
3916   Id nstack;
3917   Id firstidx;
3918 };
3919
3920 /* Tarjan's SCC algorithm, slightly modifed */
3921 static void
3922 trj_visit(struct trj_data *trj, Id node)
3923 {
3924   Id *low = trj->low;
3925   Queue *edges = trj->edges;
3926   Id nnode, myidx, stackstart;
3927   int i;
3928
3929   low[node] = myidx = trj->idx++;
3930   low[(stackstart = trj->nstack++)] = node;
3931   for (i = edges->elements[node]; (nnode = edges->elements[i]) != 0; i++)
3932     {
3933       Id l = low[nnode];
3934       if (!l)
3935         {
3936           if (!edges->elements[edges->elements[nnode]])
3937             {
3938               trj->idx++;
3939               low[nnode] = -1;
3940               continue;
3941             }
3942           trj_visit(trj, nnode);
3943           l = low[nnode];
3944         }
3945       if (l < 0)
3946         continue;
3947       if (l < trj->firstidx)
3948         {
3949           int k;
3950           for (k = l; low[low[k]] == l; k++)
3951             low[low[k]] = -1;
3952         }
3953       else if (l < low[node])
3954         low[node] = l;
3955     }
3956   if (low[node] == myidx)
3957     {
3958       if (myidx != trj->firstidx)
3959         myidx = -1;
3960       for (i = stackstart; i < trj->nstack; i++)
3961         low[low[i]] = myidx;
3962       trj->nstack = stackstart;
3963     }
3964 }
3965
3966
3967 void
3968 solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered)
3969 {
3970   Repo *installed = solv->installed;
3971   int i;
3972   Map cleandepsmap;
3973
3974   queue_empty(unneededq);
3975   if (!installed || installed->end == installed->start)
3976     return;
3977
3978   map_init(&cleandepsmap, installed->end - installed->start);
3979   solver_createcleandepsmap(solv, &cleandepsmap, 1);
3980   for (i = installed->start; i < installed->end; i++)
3981     if (MAPTST(&cleandepsmap, i - installed->start))
3982       queue_push(unneededq, i);
3983
3984   if (filtered && unneededq->count > 1)
3985     {
3986       Pool *pool = solv->pool;
3987       Queue edges;
3988       Id *nrequires;
3989       Map installedm;
3990       int j, pass, count = unneededq->count;
3991       Id *low;
3992
3993       map_init(&installedm, pool->nsolvables);
3994       for (i = installed->start; i < installed->end; i++)
3995         if (pool->solvables[i].repo == installed)
3996           MAPSET(&installedm, i);
3997
3998       nrequires = solv_calloc(count, sizeof(Id));
3999       queue_init(&edges);
4000       queue_prealloc(&edges, count * 4 + 10);   /* pre-size */
4001
4002       /*
4003        * Go through the solvables in the nodes queue and create edges for
4004        * all requires/recommends/supplements between the nodes.
4005        * The edges are stored in the edges queue, we add 1 to the node
4006        * index so that nodes in the edges queue are != 0 and we can
4007        * terminate the edge list with 0.
4008        * Thus for node element 5, the edges are stored starting at
4009        * edges.elements[6] and are 0-terminated.
4010        */
4011       /* leave first element zero to make things easier */
4012       /* also add trailing zero */
4013       queue_insertn(&edges, 0, 1 + count + 1, 0);
4014
4015       /* first requires and recommends */
4016       for (i = 0; i < count; i++)
4017         {
4018           Solvable *s = pool->solvables + unneededq->elements[i];
4019           edges.elements[i + 1] = edges.count;
4020           for (pass = 0; pass < 2; pass++)
4021             {
4022               int num = 0;
4023               unsigned int off = pass == 0 ? s->requires : s->recommends;
4024               Id p, pp, *dp;
4025               if (off)
4026                 for (dp = s->repo->idarraydata + off; *dp; dp++)
4027                   FOR_PROVIDES(p, pp, *dp)
4028                     {
4029                       Solvable *sp = pool->solvables + p;
4030                       if (s == sp || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
4031                         continue;
4032                       for (j = 0; j < count; j++)
4033                         if (p == unneededq->elements[j])
4034                           break;
4035                       if (j == count)
4036                         continue;
4037                       if (num && edges.elements[edges.count - 1] == j + 1)
4038                         continue;
4039                       queue_push(&edges, j + 1);
4040                       num++;
4041                     }
4042                 if (pass == 0)
4043                   nrequires[i] = num;
4044             }
4045           queue_push(&edges, 0);
4046         }
4047 #if 0
4048       printf("requires + recommends\n");
4049       for (i = 0; i < count; i++)
4050         {
4051           int j;
4052           printf("  %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
4053           for (j = edges.elements[i + 1]; edges.elements[j]; j++)
4054             printf("    - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
4055         }
4056 #endif
4057
4058       /* then add supplements */
4059       for (i = 0; i < count; i++)
4060         {
4061           Solvable *s = pool->solvables + unneededq->elements[i];
4062           if (s->supplements)
4063             {
4064               Id *dp;
4065               int k;
4066               for (dp = s->repo->idarraydata + s->supplements; *dp; dp++)
4067                 if (dep_possible(solv, *dp, &installedm))
4068                   {
4069                     Queue iq;
4070                     Id iqbuf[16];
4071                     queue_init_buffer(&iq, iqbuf, sizeof(iqbuf)/sizeof(*iqbuf));
4072                     dep_pkgcheck(solv, *dp, 0, &iq);
4073                     for (k = 0; k < iq.count; k++)
4074                       {
4075                         Id p = iq.elements[k];
4076                         Solvable *sp = pool->solvables + p;
4077                         if (p == unneededq->elements[i] || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
4078                           continue;
4079                         for (j = 0; j < count; j++)
4080                           if (p == unneededq->elements[j])
4081                             break;
4082                         /* now add edge from j + 1 to i + 1 */
4083                         queue_insert(&edges, edges.elements[j + 1] + nrequires[j], i + 1);
4084                         /* addapt following edge pointers */
4085                         for (j = j + 2; j < count + 1; j++)
4086                           edges.elements[j]++;
4087                       }
4088                     queue_free(&iq);
4089                   }
4090             }
4091         }
4092 #if 0
4093       /* print result */
4094       printf("+ supplements\n");
4095       for (i = 0; i < count; i++)
4096         {
4097           int j;
4098           printf("  %s (%d requires):\n", pool_solvid2str(pool, unneededq->elements[i]), nrequires[i]);
4099           for (j = edges.elements[i + 1]; edges.elements[j]; j++)
4100             printf("    - %s\n", pool_solvid2str(pool, unneededq->elements[edges.elements[j] - 1]));
4101         }
4102 #endif
4103       map_free(&installedm);
4104
4105       /* now run SCC algo two times, first with requires+recommends+supplements,
4106        * then again without the requires. We run it the second time to get rid
4107        * of packages that got dragged in via recommends/supplements */
4108       /*
4109        * low will contain the result of the SCC search.
4110        * it must be of at least size 2 * (count + 1) and
4111        * must be zero initialized.
4112        * The layout is:
4113        *    0  low low ... low stack stack ...stack 0
4114        *            count              count
4115        */
4116       low = solv_calloc(count + 1, 2 * sizeof(Id));
4117       for (pass = 0; pass < 2; pass++)
4118         {
4119           struct trj_data trj;
4120           if (pass)
4121             {
4122               memset(low, 0, (count + 1) * (2 * sizeof(Id)));
4123               for (i = 0; i < count; i++)
4124                 {
4125                   edges.elements[i + 1] += nrequires[i];
4126                   if (!unneededq->elements[i])
4127                     low[i + 1] = -1;    /* ignore this node */
4128                 }
4129             }
4130           trj.edges = &edges;
4131           trj.low = low;
4132           trj.idx = count + 1;  /* stack starts here */
4133           for (i = 1; i <= count; i++)
4134             {
4135               if (low[i])
4136                 continue;
4137               if (edges.elements[edges.elements[i]])
4138                 {
4139                   trj.firstidx = trj.nstack = trj.idx;
4140                   trj_visit(&trj, i);
4141                 }
4142               else
4143                 {
4144                   Id myidx = trj.idx++;
4145                   low[i] = myidx;
4146                   low[myidx] = i;
4147                 }
4148             }
4149           /* prune packages */
4150           for (i = 0; i < count; i++)
4151             if (low[i + 1] <= 0)
4152               unneededq->elements[i] = 0;
4153         }
4154       solv_free(low);
4155       solv_free(nrequires);
4156       queue_free(&edges);
4157
4158       /* finally remove all pruned entries from unneededq */
4159       for (i = j = 0; i < count; i++)
4160         if (unneededq->elements[i])
4161           unneededq->elements[j++] = unneededq->elements[i];
4162       queue_truncate(unneededq, j);
4163     }
4164   map_free(&cleandepsmap);
4165 }
4166
4167 /* EOF */