Merge branch 'master' of git@git.opensuse.org:projects/zypp/sat-solver
[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 "bitmap.h"
22 #include "pool.h"
23 #include "util.h"
24 #include "policy.h"
25 #include "solverdebug.h"
26
27 #define RULES_BLOCK 63
28
29 static void addrpmruleinfo(Solver *solv, Id p, Id d, int type, Id dep);
30
31 /*-------------------------------------------------------------------
32  * Check if dependency is possible
33  * 
34  * mirrors solver_dep_fulfilled but uses map m instead of the decisionmap
35  */
36
37 static inline int
38 dep_possible(Solver *solv, Id dep, Map *m)
39 {
40   Pool *pool = solv->pool;
41   Id p, pp;
42
43   if (ISRELDEP(dep))
44     {
45       Reldep *rd = GETRELDEP(pool, dep);
46       if (rd->flags == REL_AND)
47         {
48           if (!dep_possible(solv, rd->name, m))
49             return 0;
50           return dep_possible(solv, rd->evr, m);
51         }
52       if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
53         return solver_splitprovides(solv, rd->evr);
54       if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_INSTALLED)
55         return solver_dep_installed(solv, rd->evr);
56     }
57   FOR_PROVIDES(p, pp, dep)
58     {
59       if (MAPTST(m, p))
60         return 1;
61     }
62   return 0;
63 }
64
65 /********************************************************************
66  *
67  * Rule handling
68  *
69  * - unify rules, remove duplicates
70  */
71
72 /*-------------------------------------------------------------------
73  *
74  * compare rules for unification sort
75  *
76  */
77
78 static int
79 unifyrules_sortcmp(const void *ap, const void *bp, void *dp)
80 {
81   Pool *pool = dp;
82   Rule *a = (Rule *)ap;
83   Rule *b = (Rule *)bp;
84   Id *ad, *bd;
85   int x;
86
87   x = a->p - b->p;
88   if (x)
89     return x;                          /* p differs */
90
91   /* identical p */
92   if (a->d == 0 && b->d == 0)
93     return a->w2 - b->w2;              /* assertion: return w2 diff */
94
95   if (a->d == 0)                       /* a is assertion, b not */
96     {
97       x = a->w2 - pool->whatprovidesdata[b->d];
98       return x ? x : -1;
99     }
100
101   if (b->d == 0)                       /* b is assertion, a not */
102     {
103       x = pool->whatprovidesdata[a->d] - b->w2;
104       return x ? x : 1;
105     }
106
107   /* compare whatprovidesdata */
108   ad = pool->whatprovidesdata + a->d;
109   bd = pool->whatprovidesdata + b->d;
110   while (*bd)
111     if ((x = *ad++ - *bd++) != 0)
112       return x;
113   return *ad;
114 }
115
116 int
117 solver_samerule(Solver *solv, Rule *r1, Rule *r2)
118 {
119   return unifyrules_sortcmp(r1, r2, solv->pool);
120 }
121
122
123 /*-------------------------------------------------------------------
124  *
125  * unify rules
126  * go over all rules and remove duplicates
127  */
128
129 void
130 solver_unifyrules(Solver *solv)
131 {
132   Pool *pool = solv->pool;
133   int i, j;
134   Rule *ir, *jr;
135
136   if (solv->nrules <= 1)               /* nothing to unify */
137     return;
138
139   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- unifyrules -----\n");
140
141   /* sort rules first */
142   sat_sort(solv->rules + 1, solv->nrules - 1, sizeof(Rule), unifyrules_sortcmp, solv->pool);
143
144   /* prune rules
145    * i = unpruned
146    * j = pruned
147    */
148   jr = 0;
149   for (i = j = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
150     {
151       if (jr && !unifyrules_sortcmp(ir, jr, pool))
152         continue;                      /* prune! */
153       jr = solv->rules + j++;          /* keep! */
154       if (ir != jr)
155         *jr = *ir;
156     }
157
158   /* reduced count from nrules to j rules */
159   POOL_DEBUG(SAT_DEBUG_STATS, "pruned rules from %d to %d\n", solv->nrules, j);
160
161   /* adapt rule buffer */
162   solv->nrules = j;
163   solv->rules = sat_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
164
165   /*
166    * debug: log rule statistics
167    */
168   IF_POOLDEBUG (SAT_DEBUG_STATS)
169     {
170       int binr = 0;
171       int lits = 0;
172       Id *dp;
173       Rule *r;
174
175       for (i = 1; i < solv->nrules; i++)
176         {
177           r = solv->rules + i;
178           if (r->d == 0)
179             binr++;
180           else
181             {
182               dp = solv->pool->whatprovidesdata + r->d;
183               while (*dp++)
184                 lits++;
185             }
186         }
187       POOL_DEBUG(SAT_DEBUG_STATS, "  binary: %d\n", binr);
188       POOL_DEBUG(SAT_DEBUG_STATS, "  normal: %d, %d literals\n", solv->nrules - 1 - binr, lits);
189     }
190   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- unifyrules end -----\n");
191 }
192
193 #if 0
194
195 /*
196  * hash rule
197  */
198
199 static Hashval
200 hashrule(Solver *solv, Id p, Id d, int n)
201 {
202   unsigned int x = (unsigned int)p;
203   int *dp;
204
205   if (n <= 1)
206     return (x * 37) ^ (unsigned int)d;
207   dp = solv->pool->whatprovidesdata + d;
208   while (*dp)
209     x = (x * 37) ^ (unsigned int)*dp++;
210   return x;
211 }
212 #endif
213
214
215 /*-------------------------------------------------------------------
216  * 
217  */
218
219 /*
220  * add rule
221  *  p = direct literal; always < 0 for installed rpm rules
222  *  d, if < 0 direct literal, if > 0 offset into whatprovides, if == 0 rule is assertion (look at p only)
223  *
224  *
225  * A requires b, b provided by B1,B2,B3 => (-A|B1|B2|B3)
226  *
227  * p < 0 : pkg id of A
228  * d > 0 : Offset in whatprovidesdata (list of providers of b)
229  *
230  * A conflicts b, b provided by B1,B2,B3 => (-A|-B1), (-A|-B2), (-A|-B3)
231  * p < 0 : pkg id of A
232  * d < 0 : Id of solvable (e.g. B1)
233  *
234  * d == 0: unary rule, assertion => (A) or (-A)
235  *
236  *   Install:    p > 0, d = 0   (A)             user requested install
237  *   Remove:     p < 0, d = 0   (-A)            user requested remove (also: uninstallable)
238  *   Requires:   p < 0, d > 0   (-A|B1|B2|...)  d: <list of providers for requirement of p>
239  *   Updates:    p > 0, d > 0   (A|B1|B2|...)   d: <list of updates for solvable p>
240  *   Conflicts:  p < 0, d < 0   (-A|-B)         either p (conflict issuer) or d (conflict provider) (binary rule)
241  *                                              also used for obsoletes
242  *   ?:          p > 0, d < 0   (A|-B)          
243  *   No-op ?:    p = 0, d = 0   (null)          (used as policy rule placeholder)
244  *
245  *   resulting watches:
246  *   ------------------
247  *   Direct assertion (no watch needed)( if d <0 ) --> d = 0, w1 = p, w2 = 0
248  *   Binary rule: p = first literal, d = 0, w2 = second literal, w1 = p
249  *   every other : w1 = p, w2 = whatprovidesdata[d];
250  *   Disabled rule: w1 = 0
251  *
252  *   always returns a rule for non-rpm rules
253  */
254
255 Rule *
256 solver_addrule(Solver *solv, Id p, Id d)
257 {
258   Pool *pool = solv->pool;
259   Rule *r = 0;
260   Id *dp = 0;
261
262   int n = 0;                           /* number of literals in rule - 1
263                                           0 = direct assertion (single literal)
264                                           1 = binary rule
265                                           >1 = 
266                                         */
267
268   /* it often happenes that requires lead to adding the same rpm rule
269    * multiple times, so we prune those duplicates right away to make
270    * the work for unifyrules a bit easier */
271
272   if (solv->nrules                      /* we already have rules */
273       && !solv->rpmrules_end)           /* but are not done with rpm rules */
274     {
275       r = solv->rules + solv->nrules - 1;   /* get the last added rule */
276       if (r->p == p && r->d == d && d != 0)   /* identical and not user requested */
277         return r;
278     }
279
280     /*
281      * compute number of literals (n) in rule
282      */
283     
284   if (d < 0)
285     {
286       /* always a binary rule */
287       if (p == d)
288         return 0;                      /* ignore self conflict */
289       n = 1;
290     }
291   else if (d > 0)
292     {
293       for (dp = pool->whatprovidesdata + d; *dp; dp++, n++)
294         if (*dp == -p)
295           return 0;                     /* rule is self-fulfilling */
296         
297       if (n == 1)   /* have single provider */
298         d = dp[-1];                     /* take single literal */
299     }
300
301   if (n == 1 && p > d && !solv->rpmrules_end)
302     {
303       /* smallest literal first so we can find dups */
304       n = p; p = d; d = n;             /* p <-> d */
305       n = 1;                           /* re-set n, was used as temp var */
306     }
307
308   /*
309    * check for duplicate
310    */
311     
312   /* check if the last added rule (r) is exactly the same as what we're looking for. */
313   if (r && n == 1 && !r->d && r->p == p && r->w2 == d)
314     return r;  /* binary rule */
315
316     /* have n-ary rule with same first literal, check other literals */
317   if (r && n > 1 && r->d && r->p == p)
318     {
319       /* Rule where d is an offset in whatprovidesdata */
320       Id *dp2;
321       if (d == r->d)
322         return r;
323       dp2 = pool->whatprovidesdata + r->d;
324       for (dp = pool->whatprovidesdata + d; *dp; dp++, dp2++)
325         if (*dp != *dp2)
326           break;
327       if (*dp == *dp2)
328         return r;
329    }
330
331   /*
332    * allocate new rule
333    */
334
335   /* extend rule buffer */
336   solv->rules = sat_extend(solv->rules, solv->nrules, 1, sizeof(Rule), RULES_BLOCK);
337   r = solv->rules + solv->nrules++;    /* point to rule space */
338
339     /*
340      * r = new rule
341      */
342     
343   r->p = p;
344   if (n == 0)
345     {
346       /* direct assertion, no watch needed */
347       r->d = 0;
348       r->w1 = p;
349       r->w2 = 0;
350     }
351   else if (n == 1)
352     {
353       /* binary rule */
354       r->d = 0;
355       r->w1 = p;
356       r->w2 = d;
357     }
358   else
359     {
360       r->d = d;
361       r->w1 = p;
362       r->w2 = pool->whatprovidesdata[d];
363     }
364   r->n1 = 0;
365   r->n2 = 0;
366
367   IF_POOLDEBUG (SAT_DEBUG_RULE_CREATION)
368     {
369       POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "  Add rule: ");
370       solver_printrule(solv, SAT_DEBUG_RULE_CREATION, r);
371     }
372
373   return r;
374 }
375
376
377 /******************************************************************************
378  ***
379  *** rpm rule part: create rules representing the package dependencies
380  ***
381  ***/
382
383 /*
384  *  special multiversion patch conflict handling:
385  *  a patch conflict is also satisfied, if some other
386  *  version with the same name/arch that doesn't conflict
387  *  get's installed. The generated rule is thus:
388  *  -patch|-cpack|opack1|opack2|...
389  */
390 static Id
391 makemultiversionconflict(Solver *solv, Id n, Id con)
392 {
393   Pool *pool = solv->pool;
394   Solvable *s, *sn;
395   Queue q;
396   Id p, pp, qbuf[64];
397
398   sn = pool->solvables + n;
399   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
400   queue_push(&q, -n);
401   FOR_PROVIDES(p, pp, sn->name)
402     {
403       s = pool->solvables + p;
404       if (s->name != sn->name || s->arch != sn->arch)
405         continue;
406       if (!MAPTST(&solv->noobsoletes, p))
407         continue;
408       if (pool_match_nevr(pool, pool->solvables + p, con))
409         continue;
410       /* here we have a multiversion solvable that doesn't conflict */
411       /* thus we're not in conflict if it is installed */
412       queue_push(&q, p);
413     }
414   if (q.count == 1)
415     return -n;  /* no other package found, generate normal conflict */
416   return pool_queuetowhatprovides(pool, &q);
417 }
418
419 static inline void
420 addrpmrule(Solver *solv, Id p, Id d, int type, Id dep)
421 {
422   if (!solv->ruleinfoq)
423     solver_addrule(solv, p, d);
424   else
425     addrpmruleinfo(solv, p, d, type, dep);
426 }
427
428 /*-------------------------------------------------------------------
429  * 
430  * add (install) rules for solvable
431  * 
432  * s: Solvable for which to add rules
433  * m: m[s] = 1 for solvables which have rules, prevent rule duplication
434  * 
435  * Algorithm: 'visit all nodes of a graph'. The graph nodes are
436  *  solvables, the edges their dependencies.
437  *  Starting from an installed solvable, this will create all rules
438  *  representing the graph created by the solvables dependencies.
439  * 
440  * for unfulfilled requirements, conflicts, obsoletes,....
441  * add a negative assertion for solvables that are not installable
442  * 
443  * It will also create rules for all solvables referenced by 's'
444  *  i.e. descend to all providers of requirements of 's'
445  *
446  */
447
448 void
449 solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
450 {
451   Pool *pool = solv->pool;
452   Repo *installed = solv->installed;
453
454   /* 'work' queue. keeps Ids of solvables we still have to work on.
455      And buffer for it. */
456   Queue workq;
457   Id workqbuf[64];
458     
459   int i;
460     /* if to add rules for broken deps ('rpm -V' functionality)
461      * 0 = yes, 1 = no
462      */
463   int dontfix;
464     /* Id var and pointer for each dependency
465      * (not used in parallel)
466      */
467   Id req, *reqp;
468   Id con, *conp;
469   Id obs, *obsp;
470   Id rec, *recp;
471   Id sug, *sugp;
472   Id p, pp;             /* whatprovides loops */
473   Id *dp;               /* ptr to 'whatprovides' */
474   Id n;                 /* Id for current solvable 's' */
475
476   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforsolvable -----\n");
477
478   queue_init_buffer(&workq, workqbuf, sizeof(workqbuf)/sizeof(*workqbuf));
479   queue_push(&workq, s - pool->solvables);      /* push solvable Id to work queue */
480
481   /* loop until there's no more work left */
482   while (workq.count)
483     {
484       /*
485        * n: Id of solvable
486        * s: Pointer to solvable
487        */
488
489       n = queue_shift(&workq);          /* 'pop' next solvable to work on from queue */
490       if (m)
491         {
492           if (MAPTST(m, n))             /* continue if already visited */
493             continue;
494           MAPSET(m, n);                 /* mark as visited */
495         }
496
497       s = pool->solvables + n;          /* s = Solvable in question */
498
499       dontfix = 0;
500       if (installed                     /* Installed system available */
501           && !solv->fixsystem           /* NOT repair errors in rpm dependency graph */
502           && s->repo == installed)      /* solvable is installed? */
503         {
504           dontfix = 1;                  /* dont care about broken rpm deps */
505         }
506
507       if (!dontfix
508           && s->arch != ARCH_SRC
509           && s->arch != ARCH_NOSRC
510           && !pool_installable(pool, s))
511         {
512           POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "package %s [%d] is not installable\n", solvable2str(pool, s), (Id)(s - pool->solvables));
513           addrpmrule(solv, -n, 0, SOLVER_RULE_RPM_NOT_INSTALLABLE, 0);
514         }
515
516       /* yet another SUSE hack, sigh */
517       if (pool->nscallback && !strncmp("product:", id2str(pool, s->name), 8))
518         {
519           Id buddy = pool->nscallback(pool, pool->nscallbackdata, NAMESPACE_PRODUCTBUDDY, n);
520           if (buddy > 0 && buddy != SYSTEMSOLVABLE && buddy != n && buddy < pool->nsolvables)
521             {
522               addrpmrule(solv, n, -buddy, SOLVER_RULE_RPM_PACKAGE_REQUIRES, solvable_selfprovidedep(pool->solvables + n));
523               addrpmrule(solv, buddy, -n, SOLVER_RULE_RPM_PACKAGE_REQUIRES, solvable_selfprovidedep(pool->solvables + buddy)); 
524               if (m && !MAPTST(m, buddy))
525                 queue_push(&workq, buddy);
526             }
527         }
528
529       /*-----------------------------------------
530        * check requires of s
531        */
532
533       if (s->requires)
534         {
535           reqp = s->repo->idarraydata + s->requires;
536           while ((req = *reqp++) != 0)            /* go through all requires */
537             {
538               if (req == SOLVABLE_PREREQMARKER)   /* skip the marker */
539                 continue;
540
541               /* find list of solvables providing 'req' */
542               dp = pool_whatprovides_ptr(pool, req);
543
544               if (*dp == SYSTEMSOLVABLE)          /* always installed */
545                 continue;
546
547               if (dontfix)
548                 {
549                   /* the strategy here is to not insist on dependencies
550                    * that are already broken. so if we find one provider
551                    * that was already installed, we know that the
552                    * dependency was not broken before so we enforce it */
553                  
554                   /* check if any of the providers for 'req' is installed */
555                   for (i = 0; (p = dp[i]) != 0; i++)
556                     {
557                       if (pool->solvables[p].repo == installed)
558                         break;          /* provider was installed */
559                     }
560                   /* didn't find an installed provider: previously broken dependency */
561                   if (!p)
562                     {
563                       POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "ignoring broken requires %s of installed package %s\n", dep2str(pool, req), solvable2str(pool, s));
564                       continue;
565                     }
566                 }
567
568               if (!*dp)
569                 {
570                   /* nothing provides req! */
571                   POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", solvable2str(pool, s), (Id)(s - pool->solvables), dep2str(pool, req));
572                   addrpmrule(solv, -n, 0, SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP, req);
573                   continue;
574                 }
575
576               IF_POOLDEBUG (SAT_DEBUG_RULE_CREATION)
577                 {
578                   POOL_DEBUG(SAT_DEBUG_RULE_CREATION,"  %s requires %s\n", solvable2str(pool, s), dep2str(pool, req));
579                   for (i = 0; dp[i]; i++)
580                     POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "   provided by %s\n", solvid2str(pool, dp[i]));
581                 }
582
583               /* add 'requires' dependency */
584               /* rule: (-requestor|provider1|provider2|...|providerN) */
585               addrpmrule(solv, -n, dp - pool->whatprovidesdata, SOLVER_RULE_RPM_PACKAGE_REQUIRES, req);
586
587               /* descend the dependency tree
588                  push all non-visited providers on the work queue */
589               if (m)
590                 {
591                   for (; *dp; dp++)
592                     {
593                       if (!MAPTST(m, *dp))
594                         queue_push(&workq, *dp);
595                     }
596                 }
597
598             } /* while, requirements of n */
599
600         } /* if, requirements */
601
602       /* that's all we check for src packages */
603       if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
604         continue;
605
606       /*-----------------------------------------
607        * check conflicts of s
608        */
609
610       if (s->conflicts)
611         {
612           int ispatch = 0;
613
614           /* we treat conflicts in patches a bit differen:
615            * - nevr matching
616            * - multiversion handling
617            * XXX: we should really handle this different, looking
618            * at the name is a bad hack
619            */
620           if (!strncmp("patch:", id2str(pool, s->name), 6))
621             ispatch = 1;
622           conp = s->repo->idarraydata + s->conflicts;
623           /* foreach conflicts of 's' */
624           while ((con = *conp++) != 0)
625             {
626               /* foreach providers of a conflict of 's' */
627               FOR_PROVIDES(p, pp, con)
628                 {
629                   if (ispatch && !pool_match_nevr(pool, pool->solvables + p, con))
630                     continue;
631                   /* dontfix: dont care about conflicts with already installed packs */
632                   if (dontfix && pool->solvables[p].repo == installed)
633                     continue;
634                   /* p == n: self conflict */
635                   if (p == n && !pool->allowselfconflicts)
636                     {
637                       if (ISRELDEP(con))
638                         {
639                           Reldep *rd = GETRELDEP(pool, con);
640                           if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
641                             continue;
642                         }
643                       p = 0;    /* make it a negative assertion, aka 'uninstallable' */
644                     }
645                   if (p && ispatch && solv->noobsoletes.size && MAPTST(&solv->noobsoletes, p) && ISRELDEP(con))
646                     {
647                       /* our patch conflicts with a noobsoletes (aka multiversion) package */
648                       p = -makemultiversionconflict(solv, p, con);
649                     }
650                  /* rule: -n|-p: either solvable _or_ provider of conflict */
651                   addrpmrule(solv, -n, -p, p ? SOLVER_RULE_RPM_PACKAGE_CONFLICT : SOLVER_RULE_RPM_SELF_CONFLICT, con);
652                 }
653             }
654         }
655
656       /*-----------------------------------------
657        * check obsoletes if not installed
658        * (only installation will trigger the obsoletes in rpm)
659        */
660       if (!installed || pool->solvables[n].repo != installed)
661         {                              /* not installed */
662           int noobs = solv->noobsoletes.size && MAPTST(&solv->noobsoletes, n);
663           if (s->obsoletes && !noobs)
664             {
665               obsp = s->repo->idarraydata + s->obsoletes;
666               /* foreach obsoletes */
667               while ((obs = *obsp++) != 0)
668                 {
669                   /* foreach provider of an obsoletes of 's' */ 
670                   FOR_PROVIDES(p, pp, obs)
671                     {
672                       if (!pool->obsoleteusesprovides /* obsoletes are matched names, not provides */
673                           && !pool_match_nevr(pool, pool->solvables + p, obs))
674                         continue;
675                       addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_PACKAGE_OBSOLETES, obs);
676                     }
677                 }
678             }
679           FOR_PROVIDES(p, pp, s->name)
680             {
681               Solvable *ps = pool->solvables + p;
682               /* we still obsolete packages with same nevra, like rpm does */
683               /* (actually, rpm mixes those packages. yuck...) */
684               if (noobs && (s->name != ps->name || s->evr != ps->evr || s->arch != ps->arch))
685                 continue;
686               if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
687                 continue;
688               if (s->name == ps->name)
689                 addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_SAME_NAME, 0);
690               else
691                 addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_IMPLICIT_OBSOLETES, s->name);
692             }
693         }
694
695       /*-----------------------------------------
696        * add recommends to the work queue
697        */
698       if (s->recommends && m)
699         {
700           recp = s->repo->idarraydata + s->recommends;
701           while ((rec = *recp++) != 0)
702             {
703               FOR_PROVIDES(p, pp, rec)
704                 if (!MAPTST(m, p))
705                   queue_push(&workq, p);
706             }
707         }
708       if (s->suggests && m)
709         {
710           sugp = s->repo->idarraydata + s->suggests;
711           while ((sug = *sugp++) != 0)
712             {
713               FOR_PROVIDES(p, pp, sug)
714                 if (!MAPTST(m, p))
715                   queue_push(&workq, p);
716             }
717         }
718     }
719   queue_free(&workq);
720   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforsolvable end -----\n");
721 }
722
723
724 /*-------------------------------------------------------------------
725  * 
726  * Add package rules for weak rules
727  *
728  * m: visited solvables
729  */
730
731 void
732 solver_addrpmrulesforweak(Solver *solv, Map *m)
733 {
734   Pool *pool = solv->pool;
735   Solvable *s;
736   Id sup, *supp;
737   int i, n;
738
739   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforweak -----\n");
740     /* foreach solvable in pool */
741   for (i = n = 1; n < pool->nsolvables; i++, n++)
742     {
743       if (i == pool->nsolvables)                /* wrap i */
744         i = 1;
745       if (MAPTST(m, i))                         /* been there */
746         continue;
747
748       s = pool->solvables + i;
749       if (!pool_installable(pool, s))           /* only look at installable ones */
750         continue;
751
752       sup = 0;
753       if (s->supplements)
754         {
755           /* find possible supplements */
756           supp = s->repo->idarraydata + s->supplements;
757           while ((sup = *supp++) != ID_NULL)
758             if (dep_possible(solv, sup, m))
759               break;
760         }
761
762       /* if nothing found, check for enhances */
763       if (!sup && s->enhances)
764         {
765           supp = s->repo->idarraydata + s->enhances;
766           while ((sup = *supp++) != ID_NULL)
767             if (dep_possible(solv, sup, m))
768               break;
769         }
770       /* if nothing found, goto next solvables */
771       if (!sup)
772         continue;
773       solver_addrpmrulesforsolvable(solv, s, m);
774       n = 0;                    /* check all solvables again */
775     }
776   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforweak end -----\n");
777 }
778
779
780 /*-------------------------------------------------------------------
781  * 
782  * add package rules for possible updates
783  * 
784  * s: solvable
785  * m: map of already visited solvables
786  * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
787  */
788
789 void
790 solver_addrpmrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
791 {
792   Pool *pool = solv->pool;
793   int i;
794     /* queue and buffer for it */
795   Queue qs;
796   Id qsbuf[64];
797
798   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforupdaters -----\n");
799
800   queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
801     /* find update candidates for 's' */
802   policy_findupdatepackages(solv, s, &qs, allow_all);
803     /* add rule for 's' if not already done */
804   if (!MAPTST(m, s - pool->solvables))
805     solver_addrpmrulesforsolvable(solv, s, m);
806     /* foreach update candidate, add rule if not already done */
807   for (i = 0; i < qs.count; i++)
808     if (!MAPTST(m, qs.elements[i]))
809       solver_addrpmrulesforsolvable(solv, pool->solvables + qs.elements[i], m);
810   queue_free(&qs);
811
812   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforupdaters -----\n");
813 }
814
815
816 /***********************************************************************
817  ***
818  ***  Update/Feature rule part
819  ***
820  ***  Those rules make sure an installed package isn't silently deleted
821  ***
822  ***/
823
824 static Id
825 finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
826 {
827   Pool *pool = solv->pool;
828   int i;
829
830   policy_findupdatepackages(solv, s, qs, allow_all);
831   if (!qs->count)
832     {
833       if (allow_all)
834         return 0;       /* orphaned, don't create feature rule */
835       /* check if this is an orphaned package */
836       policy_findupdatepackages(solv, s, qs, 1);
837       if (!qs->count)
838         return 0;       /* orphaned, don't create update rule */
839       qs->count = 0;
840       return -SYSTEMSOLVABLE;   /* supported but not installable */
841     }
842   if (allow_all)
843     return s - pool->solvables;
844   /* check if it is ok to keep the installed package */
845   for (i = 0; i < qs->count; i++)
846     {
847       Solvable *ns = pool->solvables + qs->elements[i];
848       if (s->evr == ns->evr && solvable_identical(s, ns))
849         return s - pool->solvables;
850     }
851   /* nope, it must be some other package */
852   return -SYSTEMSOLVABLE;
853 }
854
855 /* add packages from the dup repositories to the update candidates
856  * this isn't needed for the global dup mode as all packages are
857  * from dup repos in that case */
858 static void
859 addduppackages(Solver *solv, Solvable *s, Queue *qs)
860 {
861   Queue dupqs;
862   Id p, dupqsbuf[64];
863   int i;
864   int oldnoupdateprovide = solv->noupdateprovide;
865
866   queue_init_buffer(&dupqs, dupqsbuf, sizeof(dupqsbuf)/sizeof(*dupqsbuf));
867   solv->noupdateprovide = 1;
868   policy_findupdatepackages(solv, s, &dupqs, 2);
869   solv->noupdateprovide = oldnoupdateprovide;
870   for (i = 0; i < dupqs.count; i++)
871     {
872       p = dupqs.elements[i];
873       if (MAPTST(&solv->dupmap, p))
874         queue_pushunique(qs, p);
875     }
876   queue_free(&dupqs);
877 }
878
879 /*-------------------------------------------------------------------
880  * 
881  * add rule for update
882  *   (A|A1|A2|A3...)  An = update candidates for A
883  *
884  * s = (installed) solvable
885  */
886
887 void
888 solver_addupdaterule(Solver *solv, Solvable *s, int allow_all)
889 {
890   /* installed packages get a special upgrade allowed rule */
891   Pool *pool = solv->pool;
892   Id p, d;
893   Queue qs;
894   Id qsbuf[64];
895
896   POOL_DEBUG(SAT_DEBUG_SCHUBI, "-----  addupdaterule -----\n");
897   queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
898   p = s - pool->solvables;
899   /* find update candidates for 's' */
900   if (solv->distupgrade)
901     p = finddistupgradepackages(solv, s, &qs, allow_all);
902   else
903     policy_findupdatepackages(solv, s, &qs, allow_all);
904   if (!allow_all && !solv->distupgrade && solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
905     addduppackages(solv, s, &qs);
906
907   if (!allow_all && qs.count && solv->noobsoletes.size)
908     {
909       int i, j;
910
911       d = pool_queuetowhatprovides(pool, &qs);
912       /* filter out all noobsoletes packages as they don't update */
913       for (i = j = 0; i < qs.count; i++)
914         {
915           if (MAPTST(&solv->noobsoletes, qs.elements[i]))
916             {
917               /* it's ok if they have same nevra */
918               Solvable *ps = pool->solvables + qs.elements[i];
919               if (ps->name != s->name || ps->evr != s->evr || ps->arch != s->arch)
920                 continue;
921             }
922           qs.elements[j++] = qs.elements[i];
923         }
924       if (j == 0 && p == -SYSTEMSOLVABLE && solv->distupgrade)
925         {
926           queue_push(&solv->orphaned, s - pool->solvables);     /* treat as orphaned */
927           j = qs.count;
928         }
929       if (j < qs.count)
930         {
931           if (d && solv->updatesystem && solv->installed && s->repo == solv->installed)
932             {
933               if (!solv->multiversionupdaters)
934                 solv->multiversionupdaters = sat_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
935               solv->multiversionupdaters[s - pool->solvables - solv->installed->start] = d;
936             }
937           qs.count = j;
938         }
939     }
940   if (qs.count && p == -SYSTEMSOLVABLE)
941     p = queue_shift(&qs);
942   d = qs.count ? pool_queuetowhatprovides(pool, &qs) : 0;
943   queue_free(&qs);
944   solver_addrule(solv, p, d);   /* allow update of s */
945   POOL_DEBUG(SAT_DEBUG_SCHUBI, "-----  addupdaterule end -----\n");
946 }
947
948 static inline void 
949 disableupdaterule(Solver *solv, Id p)
950 {
951   Rule *r;
952
953   MAPSET(&solv->noupdate, p - solv->installed->start);
954   r = solv->rules + solv->updaterules + (p - solv->installed->start);
955   if (r->p && r->d >= 0)
956     solver_disablerule(solv, r);
957   r = solv->rules + solv->featurerules + (p - solv->installed->start);
958   if (r->p && r->d >= 0)
959     solver_disablerule(solv, r);
960 }
961
962 static inline void 
963 reenableupdaterule(Solver *solv, Id p)
964 {
965   Pool *pool = solv->pool;
966   Rule *r;
967
968   MAPCLR(&solv->noupdate, p - solv->installed->start);
969   r = solv->rules + solv->updaterules + (p - solv->installed->start);
970   if (r->p)
971     {    
972       if (r->d >= 0)
973         return;
974       solver_enablerule(solv, r);
975       IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
976         {
977           POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
978           solver_printruleclass(solv, SAT_DEBUG_SOLUTIONS, r);
979         }
980       return;
981     }
982   r = solv->rules + solv->featurerules + (p - solv->installed->start);
983   if (r->p && r->d < 0)
984     {
985       solver_enablerule(solv, r);
986       IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
987         {
988           POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
989           solver_printruleclass(solv, SAT_DEBUG_SOLUTIONS, r);
990         }
991     }
992 }
993
994
995 /***********************************************************************
996  ***
997  ***  Infarch rule part
998  ***
999  ***  Infarch rules make sure the solver uses the best architecture of
1000  ***  a package if multiple archetectures are available
1001  ***
1002  ***/
1003
1004 void
1005 solver_addinfarchrules(Solver *solv, Map *addedmap)
1006 {
1007   Pool *pool = solv->pool;
1008   int first, i, j;
1009   Id p, pp, a, aa, bestarch;
1010   Solvable *s, *ps, *bests;
1011   Queue badq, allowedarchs;
1012
1013   queue_init(&badq);
1014   queue_init(&allowedarchs);
1015   solv->infarchrules = solv->nrules;
1016   for (i = 1; i < pool->nsolvables; i++)
1017     {
1018       if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
1019         continue;
1020       s = pool->solvables + i;
1021       first = i;
1022       bestarch = 0;
1023       bests = 0;
1024       queue_empty(&allowedarchs);
1025       FOR_PROVIDES(p, pp, s->name)
1026         {
1027           ps = pool->solvables + p;
1028           if (ps->name != s->name || !MAPTST(addedmap, p))
1029             continue;
1030           if (p == i)
1031             first = 0;
1032           if (first)
1033             break;
1034           a = ps->arch;
1035           a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
1036           if (a != 1 && pool->installed && ps->repo == pool->installed)
1037             {
1038               if (!solv->distupgrade)
1039                 queue_pushunique(&allowedarchs, ps->arch);      /* also ok to keep this architecture */
1040               continue;         /* ignore installed solvables when calculating the best arch */
1041             }
1042           if (a && a != 1 && (!bestarch || a < bestarch))
1043             {
1044               bestarch = a;
1045               bests = ps;
1046             }
1047         }
1048       if (first)
1049         continue;
1050       /* speed up common case where installed package already has best arch */
1051       if (allowedarchs.count == 1 && bests && allowedarchs.elements[0] == bests->arch)
1052         allowedarchs.count--;   /* installed arch is best */
1053       queue_empty(&badq);
1054       FOR_PROVIDES(p, pp, s->name)
1055         {
1056           ps = pool->solvables + p;
1057           if (ps->name != s->name || !MAPTST(addedmap, p))
1058             continue;
1059           a = ps->arch;
1060           a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
1061           if (a != 1 && bestarch && ((a ^ bestarch) & 0xffff0000) != 0)
1062             {
1063               for (j = 0; j < allowedarchs.count; j++)
1064                 {
1065                   aa = allowedarchs.elements[j];
1066                   if (ps->arch == aa)
1067                     break;
1068                   aa = (aa <= pool->lastarch) ? pool->id2arch[aa] : 0;
1069                   if (aa && ((a ^ aa) & 0xffff0000) == 0)
1070                     break;      /* compatible */
1071                 }
1072               if (j == allowedarchs.count)
1073                 queue_push(&badq, p);
1074             }
1075         }
1076       if (!badq.count)
1077         continue;
1078       /* block all solvables in the badq! */
1079       for (j = 0; j < badq.count; j++)
1080         {
1081           p = badq.elements[j];
1082           solver_addrule(solv, -p, 0);
1083         }
1084     }
1085   queue_free(&badq);
1086   queue_free(&allowedarchs);
1087   solv->infarchrules_end = solv->nrules;
1088 }
1089
1090 static inline void
1091 disableinfarchrule(Solver *solv, Id name)
1092 {
1093   Pool *pool = solv->pool;
1094   Rule *r;
1095   int i;
1096   for (i = solv->infarchrules, r = solv->rules + i; i < solv->infarchrules_end; i++, r++)
1097     {
1098       if (r->p < 0 && r->d >= 0 && pool->solvables[-r->p].name == name)
1099         solver_disablerule(solv, r);
1100     }
1101 }
1102
1103 static inline void
1104 reenableinfarchrule(Solver *solv, Id name)
1105 {
1106   Pool *pool = solv->pool;
1107   Rule *r;
1108   int i;
1109   for (i = solv->infarchrules, r = solv->rules + i; i < solv->infarchrules_end; i++, r++)
1110     {
1111       if (r->p < 0 && r->d < 0 && pool->solvables[-r->p].name == name)
1112         {
1113           solver_enablerule(solv, r);
1114           IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
1115             {
1116               POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
1117               solver_printruleclass(solv, SAT_DEBUG_SOLUTIONS, r);
1118             }
1119         }
1120     }
1121 }
1122
1123
1124 /***********************************************************************
1125  ***
1126  ***  Dup rule part
1127  ***
1128  ***  Dup rules make sure a package is selected from the specified dup
1129  ***  repositories if an update candidate is included in one of them.
1130  ***
1131  ***/
1132
1133 void
1134 solver_createdupmaps(Solver *solv)
1135 {
1136   Queue *job = &solv->job;
1137   Pool *pool = solv->pool;
1138   Repo *repo;
1139   Id how, what, p, pi, pp, obs, *obsp;
1140   Solvable *s, *ps;
1141   int i;
1142
1143   map_init(&solv->dupmap, pool->nsolvables);
1144   map_init(&solv->dupinvolvedmap, pool->nsolvables);
1145   for (i = 0; i < job->count; i += 2)
1146     {
1147       how = job->elements[i];
1148       what = job->elements[i + 1];
1149       switch (how & SOLVER_JOBMASK)
1150         {
1151         case SOLVER_DISTUPGRADE:
1152           if (what < 0 || what > pool->nrepos)
1153             break;
1154           repo = pool->repos[what];
1155           FOR_REPO_SOLVABLES(repo, p, s)
1156             {
1157               MAPSET(&solv->dupmap, p);
1158               FOR_PROVIDES(pi, pp, s->name)
1159                 {
1160                   ps = pool->solvables + pi;
1161                   if (ps->name != s->name)
1162                     continue;
1163                   MAPSET(&solv->dupinvolvedmap, pi);
1164                 }
1165               if (s->obsoletes)
1166                 {
1167                   /* FIXME: check obsoletes/provides combination */
1168                   obsp = s->repo->idarraydata + s->obsoletes;
1169                   while ((obs = *obsp++) != 0)
1170                     {
1171                       FOR_PROVIDES(pi, pp, obs)
1172                         {
1173                           if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + pi, obs))
1174                             continue;
1175                           MAPSET(&solv->dupinvolvedmap, pi);
1176                         }
1177                     }
1178                 }
1179             }
1180           break;
1181         default:
1182           break;
1183         }
1184     }
1185   MAPCLR(&solv->dupinvolvedmap, SYSTEMSOLVABLE);
1186 }
1187
1188 void
1189 solver_freedupmaps(Solver *solv)
1190 {
1191   map_free(&solv->dupmap);
1192   map_free(&solv->dupinvolvedmap);
1193 }
1194
1195 void
1196 solver_addduprules(Solver *solv, Map *addedmap)
1197 {
1198   Pool *pool = solv->pool;
1199   Id p, pp;
1200   Solvable *s, *ps;
1201   int first, i;
1202
1203   solv->duprules = solv->nrules;
1204   for (i = 1; i < pool->nsolvables; i++)
1205     {
1206       if (i == SYSTEMSOLVABLE || !MAPTST(addedmap, i))
1207         continue;
1208       s = pool->solvables + i;
1209       first = i;
1210       FOR_PROVIDES(p, pp, s->name)
1211         {
1212           ps = pool->solvables + p;
1213           if (ps->name != s->name || !MAPTST(addedmap, p))
1214             continue;
1215           if (p == i)
1216             first = 0;
1217           if (first)
1218             break;
1219           if (!MAPTST(&solv->dupinvolvedmap, p))
1220             continue;
1221           if (solv->installed && ps->repo == solv->installed)
1222             {
1223               if (!solv->updatemap.size)
1224                 map_init(&solv->updatemap, pool->nsolvables);
1225               MAPSET(&solv->updatemap, p);
1226               if (!MAPTST(&solv->dupmap, p))
1227                 {
1228                   Id ip, ipp;
1229                   /* is installed identical to a good one? */
1230                   FOR_PROVIDES(ip, ipp, s->name)
1231                     {
1232                       Solvable *is = pool->solvables + ip;
1233                       if (!MAPTST(&solv->dupmap, ip))
1234                         continue;
1235                       if (is->evr == s->evr && solvable_identical(s, is))
1236                         break;
1237                     }
1238                   if (!ip)
1239                     solver_addrule(solv, -p, 0);        /* no match, sorry */
1240                 }
1241             }
1242           else if (!MAPTST(&solv->dupmap, p))
1243             solver_addrule(solv, -p, 0);
1244         }
1245     }
1246   solv->duprules_end = solv->nrules;
1247 }
1248
1249
1250 static inline void
1251 disableduprule(Solver *solv, Id name)
1252 {
1253   Pool *pool = solv->pool;
1254   Rule *r;
1255   int i;
1256   for (i = solv->duprules, r = solv->rules + i; i < solv->duprules_end; i++, r++) 
1257     {    
1258       if (r->p < 0 && r->d >= 0 && pool->solvables[-r->p].name == name)
1259         solver_disablerule(solv, r);
1260     }    
1261 }
1262
1263 static inline void 
1264 reenableduprule(Solver *solv, Id name)
1265 {
1266   Pool *pool = solv->pool;
1267   Rule *r;
1268   int i;
1269   for (i = solv->duprules, r = solv->rules + i; i < solv->duprules_end; i++, r++) 
1270     {    
1271       if (r->p < 0 && r->d < 0 && pool->solvables[-r->p].name == name)
1272         {
1273           solver_enablerule(solv, r);
1274           IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
1275             {
1276               POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
1277               solver_printruleclass(solv, SAT_DEBUG_SOLUTIONS, r);
1278             }
1279         }
1280     }
1281 }
1282
1283
1284 /***********************************************************************
1285  ***
1286  ***  Policy rule disabling/reenabling
1287  ***
1288  ***  Disable all policy rules that conflict with our jobs. If a job
1289  ***  gets disabled later on, reenable the involved policy rules again.
1290  ***
1291  ***/
1292
1293 #define DISABLE_UPDATE  1
1294 #define DISABLE_INFARCH 2
1295 #define DISABLE_DUP     3
1296
1297 static void
1298 jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
1299 {
1300   Pool *pool = solv->pool;
1301   Id select, p, pp;
1302   Repo *installed;
1303   Solvable *s;
1304   int i;
1305
1306   installed = solv->installed;
1307   select = how & SOLVER_SELECTMASK;
1308   switch (how & SOLVER_JOBMASK)
1309     {
1310     case SOLVER_INSTALL:
1311       if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && solv->infarchrules != solv->infarchrules_end && ISRELDEP(what))
1312         {
1313           Reldep *rd = GETRELDEP(pool, what);
1314           if (rd->flags == REL_ARCH)
1315             {
1316               int qcnt = q->count;
1317               FOR_JOB_SELECT(p, pp, select, what)
1318                 {
1319                   s = pool->solvables + p;
1320                   /* unify names */
1321                   for (i = qcnt; i < q->count; i += 2)
1322                     if (q->elements[i + 1] == s->name)
1323                       break;
1324                   if (i < q->count)
1325                     continue;
1326                   queue_push(q, DISABLE_INFARCH);
1327                   queue_push(q, s->name);
1328                 }
1329             }
1330         }
1331       if (select != SOLVER_SOLVABLE)
1332         break;
1333       s = pool->solvables + what;
1334       if (solv->infarchrules != solv->infarchrules_end)
1335         {
1336           queue_push(q, DISABLE_INFARCH);
1337           queue_push(q, s->name);
1338         }
1339       if (solv->duprules != solv->duprules_end)
1340         {
1341           queue_push(q, DISABLE_DUP);
1342           queue_push(q, s->name);
1343         }
1344       if (!installed)
1345         return;
1346       if (solv->noobsoletes.size && MAPTST(&solv->noobsoletes, what))
1347         return;
1348       if (s->repo == installed)
1349         {
1350           queue_push(q, DISABLE_UPDATE);
1351           queue_push(q, what);
1352           return;
1353         }
1354       if (s->obsoletes)
1355         {
1356           Id obs, *obsp;
1357           obsp = s->repo->idarraydata + s->obsoletes;
1358           while ((obs = *obsp++) != 0)
1359             FOR_PROVIDES(p, pp, obs)
1360               {
1361                 Solvable *ps = pool->solvables + p;
1362                 if (ps->repo != installed)
1363                   continue;
1364                 if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
1365                   continue;
1366                 queue_push(q, DISABLE_UPDATE);
1367                 queue_push(q, p);
1368               }
1369         }
1370       FOR_PROVIDES(p, pp, s->name)
1371         {
1372           Solvable *ps = pool->solvables + p;
1373           if (ps->repo != installed)
1374             continue;
1375           if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
1376             continue;
1377           queue_push(q, DISABLE_UPDATE);
1378           queue_push(q, p);
1379         }
1380       return;
1381     case SOLVER_ERASE:
1382       if (!installed)
1383         break;
1384       FOR_JOB_SELECT(p, pp, select, what)
1385         if (pool->solvables[p].repo == installed)
1386           {
1387             queue_push(q, DISABLE_UPDATE);
1388             queue_push(q, p);
1389           }
1390       return;
1391     default:
1392       return;
1393     }
1394 }
1395
1396 /* disable all policy rules that are in conflict with our job list */
1397 void
1398 solver_disablepolicyrules(Solver *solv)
1399 {
1400   Queue *job = &solv->job;
1401   int i, j;
1402   Queue allq;
1403   Rule *r;
1404   Id lastjob = -1;
1405   Id allqbuf[128];
1406
1407   queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
1408
1409   for (i = solv->jobrules; i < solv->jobrules_end; i++)
1410     {
1411       r = solv->rules + i;
1412       if (r->d < 0)     /* disabled? */
1413         continue;
1414       j = solv->ruletojob.elements[i - solv->jobrules];
1415       if (j == lastjob)
1416         continue;
1417       lastjob = j;
1418       jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
1419     }
1420   MAPZERO(&solv->noupdate);
1421   for (i = 0; i < allq.count; i += 2)
1422     {
1423       Id type = allq.elements[i], arg = allq.elements[i + 1];
1424       switch(type)
1425         {
1426         case DISABLE_UPDATE:
1427           disableupdaterule(solv, arg);
1428           break;
1429         case DISABLE_INFARCH:
1430           disableinfarchrule(solv, arg);
1431           break;
1432         case DISABLE_DUP:
1433           disableduprule(solv, arg);
1434           break;
1435         default:
1436           break;
1437         }
1438     }
1439   queue_free(&allq);
1440 }
1441
1442 /* we just disabled job #jobidx, now reenable all policy rules that were
1443  * disabled because of this job */
1444 void
1445 solver_reenablepolicyrules(Solver *solv, int jobidx)
1446 {
1447   Queue *job = &solv->job;
1448   int i, j;
1449   Queue q, allq;
1450   Rule *r;
1451   Id lastjob = -1;
1452   Id qbuf[32], allqbuf[128];
1453
1454   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
1455   queue_init_buffer(&allq, allqbuf, sizeof(allqbuf)/sizeof(*allqbuf));
1456   jobtodisablelist(solv, job->elements[jobidx], job->elements[jobidx + 1], &q);
1457   if (!q.count)
1458     return;
1459   for (i = solv->jobrules; i < solv->jobrules_end; i++)
1460     {
1461       r = solv->rules + i;
1462       if (r->d < 0)     /* disabled? */
1463         continue;
1464       j = solv->ruletojob.elements[i - solv->jobrules];
1465       if (j == lastjob)
1466         continue;
1467       lastjob = j;
1468       jobtodisablelist(solv, job->elements[j], job->elements[j + 1], &allq);
1469     }
1470   for (j = 0; j < q.count; j += 2)
1471     {
1472       Id type = q.elements[j], arg = q.elements[j + 1];
1473       for (i = 0; i < allq.count; i += 2)
1474         if (allq.elements[i] == type && allq.elements[i + 1] == arg)
1475           break;
1476       if (i < allq.count)
1477         continue;       /* still disabled */
1478       switch(type)
1479         {
1480         case DISABLE_UPDATE:
1481           reenableupdaterule(solv, arg);
1482           break;
1483         case DISABLE_INFARCH:
1484           reenableinfarchrule(solv, arg);
1485           break;
1486         case DISABLE_DUP:
1487           reenableduprule(solv, arg);
1488           break;
1489         }
1490     }
1491   queue_free(&allq);
1492   queue_free(&q);
1493 }
1494
1495
1496 /***********************************************************************
1497  ***
1498  ***  Rule info part, tell the user what the rule is about.
1499  ***
1500  ***/
1501
1502 static void
1503 addrpmruleinfo(Solver *solv, Id p, Id d, int type, Id dep)
1504 {
1505   Pool *pool = solv->pool;
1506   Rule *r;
1507   Id w2, op, od, ow2;
1508
1509   /* check if this creates the rule we're searching for */
1510   r = solv->rules + solv->ruleinfoq->elements[0];
1511   op = r->p;
1512   od = r->d < 0 ? -r->d - 1 : r->d;
1513   ow2 = 0;
1514
1515   /* normalize */
1516   w2 = d > 0 ? 0 : d;
1517   if (p < 0 && d > 0 && (!pool->whatprovidesdata[d] || !pool->whatprovidesdata[d + 1]))
1518     {
1519       w2 = pool->whatprovidesdata[d];
1520       d = 0;
1521
1522     }
1523   if (p > 0 && d < 0)           /* this hack is used for buddy deps */
1524     {
1525       w2 = p;
1526       p = d;
1527     }
1528
1529   if (d > 0)
1530     {
1531       if (p != op && !od)
1532         return;
1533       if (d != od)
1534         {
1535           Id *dp = pool->whatprovidesdata + d;
1536           Id *odp = pool->whatprovidesdata + od;
1537           while (*dp)
1538             if (*dp++ != *odp++)
1539               return;
1540           if (*odp)
1541             return;
1542         }
1543       w2 = 0;
1544       /* handle multiversion conflict rules */
1545       if (p < 0 && pool->whatprovidesdata[d] < 0)
1546         {
1547           w2 = pool->whatprovidesdata[d];
1548           /* XXX: free memory */
1549         }
1550     }
1551   else
1552     {
1553       if (od)
1554         return;
1555       ow2 = r->w2;
1556       if (p > w2)
1557         {
1558           if (w2 != op || p != ow2)
1559             return;
1560         }
1561       else
1562         {
1563           if (p != op || w2 != ow2)
1564             return;
1565         }
1566     }
1567   /* yep, rule matches. record info */
1568   queue_push(solv->ruleinfoq, type);
1569   if (type == SOLVER_RULE_RPM_SAME_NAME)
1570     {
1571       /* we normalize same name order */
1572       queue_push(solv->ruleinfoq, op < 0 ? -op : 0);
1573       queue_push(solv->ruleinfoq, ow2 < 0 ? -ow2 : 0);
1574     }
1575   else
1576     {
1577       queue_push(solv->ruleinfoq, p < 0 ? -p : 0);
1578       queue_push(solv->ruleinfoq, w2 < 0 ? -w2 : 0);
1579     }
1580   queue_push(solv->ruleinfoq, dep);
1581 }
1582
1583 static int
1584 solver_allruleinfos_cmp(const void *ap, const void *bp, void *dp)
1585 {
1586   const Id *a = ap, *b = bp;
1587   int r;
1588
1589   r = a[0] - b[0];
1590   if (r)
1591     return r;
1592   r = a[1] - b[1];
1593   if (r)
1594     return r;
1595   r = a[2] - b[2];
1596   if (r)
1597     return r;
1598   r = a[3] - b[3];
1599   if (r)
1600     return r;
1601   return 0;
1602 }
1603
1604 int
1605 solver_allruleinfos(Solver *solv, Id rid, Queue *rq)
1606 {
1607   Pool *pool = solv->pool;
1608   Rule *r = solv->rules + rid;
1609   int i, j;
1610
1611   queue_empty(rq);
1612   if (rid <= 0 || rid >= solv->rpmrules_end)
1613     {
1614       Id type, from, to, dep;
1615       type = solver_ruleinfo(solv, rid, &from, &to, &dep);
1616       queue_push(rq, type);
1617       queue_push(rq, from);
1618       queue_push(rq, to);
1619       queue_push(rq, dep);
1620       return 1;
1621     }
1622   if (r->p >= 0)
1623     return 0;
1624   queue_push(rq, rid);
1625   solv->ruleinfoq = rq;
1626   solver_addrpmrulesforsolvable(solv, pool->solvables - r->p, 0);
1627   /* also try reverse direction for conflicts */
1628   if ((r->d == 0 || r->d == -1) && r->w2 < 0)
1629     solver_addrpmrulesforsolvable(solv, pool->solvables - r->w2, 0);
1630   solv->ruleinfoq = 0;
1631   queue_shift(rq);
1632   /* now sort & unify em */
1633   if (!rq->count)
1634     return 0;
1635   sat_sort(rq->elements, rq->count / 4, 4 * sizeof(Id), solver_allruleinfos_cmp, 0);
1636   /* throw out identical entries */
1637   for (i = j = 0; i < rq->count; i += 4)
1638     {
1639       if (j)
1640         {
1641           if (rq->elements[i] == rq->elements[j - 4] && 
1642               rq->elements[i + 1] == rq->elements[j - 3] &&
1643               rq->elements[i + 2] == rq->elements[j - 2] &&
1644               rq->elements[i + 3] == rq->elements[j - 1])
1645             continue;
1646         }
1647       rq->elements[j++] = rq->elements[i];
1648       rq->elements[j++] = rq->elements[i + 1];
1649       rq->elements[j++] = rq->elements[i + 2];
1650       rq->elements[j++] = rq->elements[i + 3];
1651     }
1652   rq->count = j;
1653   return j / 4;
1654 }
1655
1656 SolverRuleinfo
1657 solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
1658 {
1659   Pool *pool = solv->pool;
1660   Rule *r = solv->rules + rid;
1661   SolverRuleinfo type = SOLVER_RULE_UNKNOWN;
1662
1663   if (fromp)
1664     *fromp = 0;
1665   if (top)
1666     *top = 0;
1667   if (depp)
1668     *depp = 0;
1669   if (rid > 0 && rid < solv->rpmrules_end)
1670     {
1671       Queue rq;
1672       int i;
1673
1674       if (r->p >= 0)
1675         return SOLVER_RULE_RPM;
1676       if (fromp)
1677         *fromp = -r->p;
1678       queue_init(&rq);
1679       queue_push(&rq, rid);
1680       solv->ruleinfoq = &rq;
1681       solver_addrpmrulesforsolvable(solv, pool->solvables - r->p, 0);
1682       /* also try reverse direction for conflicts */
1683       if ((r->d == 0 || r->d == -1) && r->w2 < 0)
1684         solver_addrpmrulesforsolvable(solv, pool->solvables - r->w2, 0);
1685       solv->ruleinfoq = 0;
1686       type = SOLVER_RULE_RPM;
1687       for (i = 1; i < rq.count; i += 4)
1688         {
1689           Id qt, qo, qp, qd;
1690           qt = rq.elements[i];
1691           qp = rq.elements[i + 1];
1692           qo = rq.elements[i + 2];
1693           qd = rq.elements[i + 3];
1694           if (type == SOLVER_RULE_RPM || type > qt)
1695             {
1696               type = qt;
1697               if (fromp)
1698                 *fromp = qp;
1699               if (top)
1700                 *top = qo;
1701               if (depp)
1702                 *depp = qd;
1703             }
1704         }
1705       queue_free(&rq);
1706       return type;
1707     }
1708   if (rid >= solv->jobrules && rid < solv->jobrules_end)
1709     {
1710       Id jidx = solv->ruletojob.elements[rid - solv->jobrules];
1711       if (fromp)
1712         *fromp = jidx;
1713       if (top)
1714         *top = solv->job.elements[jidx];
1715       if (depp)
1716         *depp = solv->job.elements[jidx + 1];
1717       if ((r->d == 0 || r->d == -1) && r->w2 == 0 && r->p == -SYSTEMSOLVABLE && (solv->job.elements[jidx] & SOLVER_SELECTMASK) != SOLVER_SOLVABLE_ONE_OF)
1718         return SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP;
1719       return SOLVER_RULE_JOB;
1720     }
1721   if (rid >= solv->updaterules && rid < solv->updaterules_end)
1722     {
1723       if (fromp)
1724         *fromp = solv->installed->start + (rid - solv->updaterules);
1725       return SOLVER_RULE_UPDATE;
1726     }
1727   if (rid >= solv->featurerules && rid < solv->featurerules_end)
1728     {
1729       if (fromp)
1730         *fromp = solv->installed->start + (rid - solv->featurerules);
1731       return SOLVER_RULE_FEATURE;
1732     }
1733   if (rid >= solv->duprules && rid < solv->duprules_end)
1734     {
1735       if (fromp)
1736         *fromp = -r->p;
1737       if (depp)
1738         *depp = pool->solvables[-r->p].name;
1739       return SOLVER_RULE_DISTUPGRADE;
1740     }
1741   if (rid >= solv->infarchrules && rid < solv->infarchrules_end)
1742     {
1743       if (fromp)
1744         *fromp = -r->p;
1745       if (depp)
1746         *depp = pool->solvables[-r->p].name;
1747       return SOLVER_RULE_INFARCH;
1748     }
1749   if (rid >= solv->learntrules)
1750     {
1751       return SOLVER_RULE_LEARNT;
1752     }
1753   return SOLVER_RULE_UNKNOWN;
1754 }
1755
1756 /* EOF */