9e5a934d18cf73cbfc88793e5660d4d69bc6761c
[platform/upstream/libsolv.git] / src / policy.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * Generic policy interface for SAT solver
10  *
11  */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17
18 #include "solver.h"
19 #include "solver_private.h"
20 #include "evr.h"
21 #include "policy.h"
22 #include "poolvendor.h"
23 #include "poolarch.h"
24 #include "cplxdeps.h"
25
26
27 /*-----------------------------------------------------------------*/
28
29 /*
30  * prep for prune_best_version
31  *   sort by name
32  */
33
34 static int
35 prune_to_best_version_sortcmp(const void *ap, const void *bp, void *dp)
36 {
37   Pool *pool = dp;
38   int r;
39   Id a = *(Id *)ap;
40   Id b = *(Id *)bp;
41   Solvable *sa, *sb;
42
43   sa = pool->solvables + a;
44   sb = pool->solvables + b;
45   r = sa->name - sb->name;
46   if (r)
47     {
48       const char *na, *nb;
49       /* different names. We use real strcmp here so that the result
50        * is not depending on some random solvable order */
51       na = pool_id2str(pool, sa->name);
52       nb = pool_id2str(pool, sb->name);
53       return strcmp(na, nb);
54     }
55   if (sa->arch != sb->arch)
56     {
57       int aa, ab;
58       aa = (sa->arch <= pool->lastarch) ? pool->id2arch[sa->arch] : 0;
59       ab = (sb->arch <= pool->lastarch) ? pool->id2arch[sb->arch] : 0;
60       if (aa != ab && aa > 1 && ab > 1)
61         return aa - ab;         /* lowest score first */
62     }
63
64   /* the same name, bring installed solvables to the front */
65   if (pool->installed)
66     {
67       if (sa->repo == pool->installed)
68         {
69           if (sb->repo != pool->installed)
70             return -1;
71         }
72       else if (sb->repo == pool->installed)
73         return 1;       
74     }
75   /* sort by repository sub-prio (installed repo handled above) */
76   r = (sb->repo ? sb->repo->subpriority : 0) - (sa->repo ? sa->repo->subpriority : 0);
77   if (r)
78     return r;
79   /* no idea about the order, sort by id */
80   return a - b;
81 }
82
83
84 /*
85  * prune to repository with highest priority.
86  * does not prune installed solvables.
87  */
88
89 static void
90 prune_to_highest_prio(Pool *pool, Queue *plist)
91 {
92   int i, j;
93   Solvable *s;
94   int bestprio = 0, bestprioset = 0;
95
96   /* prune to highest priority */
97   for (i = 0; i < plist->count; i++)  /* find highest prio in queue */
98     {
99       s = pool->solvables + plist->elements[i];
100       if (pool->installed && s->repo == pool->installed)
101         continue;
102       if (!bestprioset || s->repo->priority > bestprio)
103         {
104           bestprio = s->repo->priority;
105           bestprioset = 1;
106         }
107     }
108   if (!bestprioset)
109     return;
110   for (i = j = 0; i < plist->count; i++) /* remove all with lower prio */
111     {
112       s = pool->solvables + plist->elements[i];
113       if (s->repo->priority == bestprio || (pool->installed && s->repo == pool->installed))
114         plist->elements[j++] = plist->elements[i];
115     }
116   plist->count = j;
117 }
118
119
120 /* installed packages involed in a dup operation can only be kept
121  * if they are identical to a non-installed one */
122 static void
123 solver_prune_installed_dup_packages(Solver *solv, Queue *plist)
124 {
125   Pool *pool = solv->pool;
126   int i, j, bestprio = 0;
127
128   /* find bestprio (again) */
129   for (i = 0; i < plist->count; i++)
130     {
131       Solvable *s = pool->solvables + plist->elements[i];
132       if (s->repo != pool->installed)
133         {
134           bestprio = s->repo->priority;
135           break;
136         }
137     }
138   if (i == plist->count)
139     return;     /* only installed packages, could not find prio */
140   for (i = j = 0; i < plist->count; i++)
141     {
142       Id p = plist->elements[i];
143       Solvable *s = pool->solvables + p;
144       if (s->repo != pool->installed && s->repo->priority < bestprio)
145         continue;
146       if (s->repo == pool->installed && (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))))
147         {
148           Id p2, pp2;
149           int keepit = 0;
150           FOR_PROVIDES(p2, pp2, s->name)
151             {
152               Solvable *s2 = pool->solvables + p2;
153               if (s2->repo == pool->installed || s2->evr != s->evr || s2->repo->priority < bestprio)
154                 continue;
155               if (!solvable_identical(s, s2))
156                 continue;
157               keepit = 1;
158               if (s2->repo->priority > bestprio)
159                 {
160                   /* new max prio! */
161                   bestprio = s2->repo->priority;
162                   j = 0;
163                 }
164             }
165           if (!keepit)
166             continue;   /* no identical package found, ignore installed package */
167         }
168       plist->elements[j++] = p;
169     }
170   if (j)
171     plist->count = j;
172 }
173
174 /*
175  * like prune_to_highest_prio, but calls solver prune_installed_dup_packages
176  * when there are dup packages
177  */
178 static inline void
179 solver_prune_to_highest_prio(Solver *solv, Queue *plist)
180 {
181   prune_to_highest_prio(solv->pool, plist);
182   if (plist->count > 1 && solv->pool->installed && (solv->dupmap_all || solv->dupinvolvedmap.size))
183     solver_prune_installed_dup_packages(solv, plist);
184 }
185
186
187 static void
188 solver_prune_to_highest_prio_per_name(Solver *solv, Queue *plist)
189 {
190   Pool *pool = solv->pool;
191   Queue pq;
192   int i, j, k;
193   Id name;
194
195   queue_init(&pq);
196   solv_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool);
197   queue_push(&pq, plist->elements[0]);
198   name = pool->solvables[pq.elements[0]].name;
199   for (i = 1, j = 0; i < plist->count; i++)
200     {
201       if (pool->solvables[plist->elements[i]].name != name)
202         {
203           if (pq.count > 2)
204             solver_prune_to_highest_prio(solv, &pq);
205           for (k = 0; k < pq.count; k++)
206             plist->elements[j++] = pq.elements[k];
207           queue_empty(&pq);
208           queue_push(&pq, plist->elements[i]);
209           name = pool->solvables[pq.elements[0]].name;
210         }
211     }
212   if (pq.count > 2)
213     solver_prune_to_highest_prio(solv, &pq);
214   for (k = 0; k < pq.count; k++)
215     plist->elements[j++] = pq.elements[k];
216   queue_free(&pq);
217   plist->count = j;
218 }
219
220
221 #ifdef ENABLE_COMPLEX_DEPS
222
223 static void
224 check_complex_dep(Solver *solv, Id dep, Map *m, Queue **cqp)
225 {
226   Pool *pool = solv->pool;
227   Queue q;
228   queue_init(&q);
229   Id p;
230   int i, qcnt;
231
232 #if 0
233   printf("check_complex_dep %s\n", pool_dep2str(pool, dep));
234 #endif
235   i = pool_normalize_complex_dep(pool, dep, &q, CPLXDEPS_EXPAND);
236   if (i == 0 || i == 1)
237     {
238       queue_free(&q);
239       return;
240     }
241   qcnt = q.count;
242   for (i = 0; i < qcnt; i++)
243     {
244       for (; (p = q.elements[i]) != 0; i++)
245         {
246           if (p < 0)
247             {
248               if (solv->decisionmap[-p] > 0)
249                 continue;
250               if (solv->decisionmap[-p] < 0)
251                 break;
252               queue_push(&q, -p);
253             }
254           if (p > 0 && qcnt == q.count)
255             MAPSET(m, p);
256         }
257       if (q.elements[i])
258         {
259 #if 0
260           printf("complex dep block cannot be true\n");
261 #endif
262           while (q.elements[i])
263             i++;
264           if (qcnt != q.count)
265             queue_truncate(&q, qcnt);
266         }
267       else if (qcnt != q.count)
268         {
269           int j, k;
270           Queue *cq = *cqp;
271 #if 0
272           printf("add new complex dep block\n");
273           for (j = qcnt; j < q.count; j++)
274             printf("  - %s\n", pool_solvid2str(pool, q.elements[j]));
275 #endif
276           if (!cq)
277             {
278               cq = solv_calloc(1, sizeof(Queue));
279               queue_init(cq);
280               *cqp = cq;
281               queue_insertn(cq, 0, 256, 0);
282             }
283           for (j = qcnt; j < q.count; j++)
284             {
285               p = q.elements[j];
286               for (k = 256; k < cq->count; k += 2)
287                 if (cq->elements[k + 1] == dep && cq->elements[k] == p)
288                   break;
289               if (k == cq->count)
290                 {
291                   queue_push2(cq, p, dep);
292                   cq->elements[p & 255] |= (1 << (p >> 8 & 31));
293                 }
294             }
295           queue_truncate(&q, qcnt);
296         }
297     }
298   queue_free(&q);
299 }
300
301 static void
302 recheck_complex_dep(Solver *solv, Id p, Map *m, Queue **cqp)
303 {
304   Queue *cq = *cqp;
305   int i;
306 #if 0
307   printf("recheck_complex_dep for package %s\n", pool_solvid2str(solv->pool, p));
308 #endif
309   for (i = 256; i < cq->count; i += 2)
310     if (cq->elements[i] == p)
311       break;
312   if (i == cq->count)
313     return;     /* false alert */
314   if (solv->decisionmap[p] <= 0)
315     return;     /* just in case... */
316   memset(cq->elements, 0, sizeof(Id) * 256);
317   for (i = 256; i < cq->count; i += 2)
318     if (cq->elements[i] == p)
319       {
320         Id dep = cq->elements[i + 1];
321         queue_deleten(cq, i, 2);
322         i -= 2;
323         check_complex_dep(solv, dep, m, &cq);
324       }
325     else
326       {
327         Id pp = cq->elements[i];
328         cq->elements[pp & 255] |= (1 << (pp >> 8 & 31));
329       }
330 }
331
332 #endif
333
334
335 void
336 policy_update_recommendsmap(Solver *solv)
337 {
338   Pool *pool = solv->pool;
339   Solvable *s;
340   Id p, pp, rec, *recp, sug, *sugp;
341
342   if (solv->recommends_index < 0)
343     {
344       MAPZERO(&solv->recommendsmap);
345       MAPZERO(&solv->suggestsmap);
346 #ifdef ENABLE_COMPLEX_DEPS
347       if (solv->recommendscplxq)
348         {
349           queue_free(solv->recommendscplxq);
350           solv->recommendscplxq = solv_free(solv->recommendscplxq);
351         }
352       if (solv->suggestscplxq)
353         {
354           queue_free(solv->suggestscplxq);
355           solv->suggestscplxq = solv_free(solv->suggestscplxq);
356         }
357 #endif
358       solv->recommends_index = 0;
359     }
360   while (solv->recommends_index < solv->decisionq.count)
361     {
362       p = solv->decisionq.elements[solv->recommends_index++];
363       if (p < 0)
364         continue;
365       s = pool->solvables + p;
366 #ifdef ENABLE_COMPLEX_DEPS
367       if (solv->recommendscplxq && solv->recommendscplxq->elements[p & 255])
368         if (solv->recommendscplxq->elements[p & 255] & (1 << (p >> 8 & 31)))
369           recheck_complex_dep(solv, p, &solv->recommendsmap, &solv->recommendscplxq);
370       if (solv->suggestscplxq && solv->suggestscplxq->elements[p & 255])
371         if (solv->suggestscplxq->elements[p & 255] & (1 << (p >> 8 & 31)))
372           recheck_complex_dep(solv, p, &solv->suggestsmap, &solv->suggestscplxq);
373 #endif
374       if (s->recommends)
375         {
376           recp = s->repo->idarraydata + s->recommends;
377           while ((rec = *recp++) != 0)
378             {
379 #ifdef ENABLE_COMPLEX_DEPS
380               if (pool_is_complex_dep(pool, rec))
381                 {
382                   check_complex_dep(solv, rec, &solv->recommendsmap, &solv->recommendscplxq);
383                   continue;
384                 }
385 #endif
386               FOR_PROVIDES(p, pp, rec)
387                 MAPSET(&solv->recommendsmap, p);
388             }
389         }
390       if (s->suggests)
391         {
392           sugp = s->repo->idarraydata + s->suggests;
393           while ((sug = *sugp++) != 0)
394             {
395 #ifdef ENABLE_COMPLEX_DEPS
396               if (pool_is_complex_dep(pool, sug))
397                 {
398                   check_complex_dep(solv, sug, &solv->suggestsmap, &solv->suggestscplxq);
399                   continue;
400                 }
401 #endif
402               FOR_PROVIDES(p, pp, sug)
403                 MAPSET(&solv->suggestsmap, p);
404             }
405         }
406     }
407 }
408
409 /*
410  * prune to recommended/suggested packages.
411  * does not prune installed packages (they are also somewhat recommended).
412  */
413
414 static void
415 prune_to_recommended(Solver *solv, Queue *plist)
416 {
417   Pool *pool = solv->pool;
418   int i, j, k, ninst;
419   Solvable *s;
420   Id p;
421
422   ninst = 0;
423   if (pool->installed)
424     {
425       for (i = 0; i < plist->count; i++)
426         {
427           p = plist->elements[i];
428           s = pool->solvables + p;
429           if (pool->installed && s->repo == pool->installed)
430             ninst++;
431         }
432     }
433   if (plist->count - ninst < 2)
434     return;
435
436   /* update our recommendsmap/suggestsmap */
437   if (solv->recommends_index < solv->decisionq.count)
438     policy_update_recommendsmap(solv);
439
440   /* prune to recommended/supplemented */
441   ninst = 0;
442   for (i = j = 0; i < plist->count; i++)
443     {
444       p = plist->elements[i];
445       s = pool->solvables + p;
446       if (pool->installed && s->repo == pool->installed)
447         {
448           ninst++;
449           if (j)
450             plist->elements[j++] = p;
451           continue;
452         }
453       if (!MAPTST(&solv->recommendsmap, p))
454         if (!solver_is_supplementing(solv, s))
455           continue;
456       if (!j && ninst)
457         {
458           for (k = 0; j < ninst; k++)
459             {
460               s = pool->solvables + plist->elements[k];
461               if (pool->installed && s->repo == pool->installed)
462                 plist->elements[j++] = plist->elements[k];
463             }
464         }
465       plist->elements[j++] = p;
466     }
467   if (j)
468     plist->count = j;
469
470   /* anything left to prune? */
471   if (plist->count - ninst < 2)
472     return;
473
474   /* prune to suggested/enhanced */
475   ninst = 0;
476   for (i = j = 0; i < plist->count; i++)
477     {
478       p = plist->elements[i];
479       s = pool->solvables + p;
480       if (pool->installed && s->repo == pool->installed)
481         {
482           ninst++;
483           if (j)
484             plist->elements[j++] = p;
485           continue;
486         }
487       if (!MAPTST(&solv->suggestsmap, p))
488         if (!solver_is_enhancing(solv, s))
489           continue;
490       if (!j && ninst)
491         {
492           for (k = 0; j < ninst; k++)
493             {
494               s = pool->solvables + plist->elements[k];
495               if (pool->installed && s->repo == pool->installed)
496                 plist->elements[j++] = plist->elements[k];
497             }
498         }
499       plist->elements[j++] = p;
500     }
501   if (j)
502     plist->count = j;
503 }
504
505 static void
506 prune_to_best_arch(const Pool *pool, Queue *plist)
507 {
508   Id a, bestscore;
509   Solvable *s;
510   int i, j;
511
512   if (!pool->id2arch || plist->count < 2)
513     return;
514   bestscore = 0;
515   for (i = 0; i < plist->count; i++)
516     {
517       s = pool->solvables + plist->elements[i];
518       a = s->arch;
519       a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
520       if (a && a != 1 && (!bestscore || a < bestscore))
521         bestscore = a;
522     }
523   if (!bestscore)
524     return;
525   for (i = j = 0; i < plist->count; i++)
526     {
527       s = pool->solvables + plist->elements[i];
528       a = s->arch;
529       if (a > pool->lastarch)
530         continue;
531       a = pool->id2arch[a];
532       /* a == 1 -> noarch */
533       if (a != 1 && ((a ^ bestscore) & 0xffff0000) != 0)
534         continue;
535       plist->elements[j++] = plist->elements[i];
536     }
537   if (j)
538     plist->count = j;
539 }
540
541
542 struct trj_data {
543   Pool *pool;
544   Queue *plist;
545   Id *stack;
546   Id nstack;
547   Id *low;
548   Id firstidx;
549   Id idx;
550 };
551
552 /* This is Tarjan's SCC algorithm, slightly modified */
553 static void
554 trj_visit(struct trj_data *trj, Id node)
555 {
556   Id *low = trj->low;
557   Pool *pool = trj->pool;
558   Queue *plist = trj->plist;
559   Id myidx, stackstart;
560   Solvable *s;
561   int i;
562   Id p, pp, obs, *obsp;
563
564   low[node] = myidx = trj->idx++;
565   trj->stack[(stackstart = trj->nstack++)] = node;
566
567   s = pool->solvables + plist->elements[node];
568   if (s->obsoletes)
569     {
570       obsp = s->repo->idarraydata + s->obsoletes;
571       while ((obs = *obsp++) != 0)
572         {
573           FOR_PROVIDES(p, pp, obs)
574             {
575               Solvable *ps = pool->solvables + p;
576               if (ps->name == s->name)
577                 continue;
578               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
579                 continue;
580               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
581                 continue;
582               /* hmm, expensive. should use hash if plist is big */
583               for (i = 0; i < plist->count; i++)
584                 {
585                   if (node != i && plist->elements[i] == p)
586                     {
587                       Id l = low[i];
588                       if (!l)
589                         {
590                           if (!ps->obsoletes)
591                             {
592                               /* don't bother */
593                               trj->idx++;
594                               low[i] = -1;
595                               continue;
596                             }
597                           trj_visit(trj, i);
598                           l = low[i];
599                         }
600                       if (l < 0)
601                         continue;
602                       if (l < trj->firstidx)
603                         {
604                           int k;
605                           /* this means we have reached an old SCC found earlier.
606                            * delete it as we obsolete it */
607                           for (k = l; ; k++)
608                             {
609                               if (low[trj->stack[k]] == l)
610                                 low[trj->stack[k]] = -1;
611                               else
612                                 break;
613                             }
614                         }
615                       else if (l < low[node])
616                         low[node] = l;
617                     }
618                 }
619             }
620         }
621     }
622   if (low[node] == myidx)       /* found a SCC? */
623     {
624       /* we're only interested in SCCs that contain the first node,
625        * as all others are "obsoleted" */
626       if (myidx != trj->firstidx)
627         myidx = -1;
628       for (i = stackstart; i < trj->nstack; i++)
629         low[trj->stack[i]] = myidx;
630       trj->nstack = stackstart; /* empty stack */
631     }
632 }
633
634 /*
635  * remove entries from plist that are obsoleted by other entries
636  * with different name.
637  */
638 static void
639 prune_obsoleted(Pool *pool, Queue *plist)
640 {
641   Id data_buf[2 * 16], *data;
642   struct trj_data trj;
643   int i, j;
644   Solvable *s;
645
646   if (plist->count <= 16)
647     {
648       memset(data_buf, 0, sizeof(data_buf));
649       data = data_buf;
650     }
651   else
652     data = solv_calloc(plist->count, 2 * sizeof(Id));
653   trj.pool = pool;
654   trj.plist = plist;
655   trj.low = data;
656   trj.idx = 1;
657   trj.stack = data + plist->count - 1;  /* -1 so we can index with idx (which starts with 1) */
658   for (i = 0; i < plist->count; i++)
659     {
660       if (trj.low[i])
661         continue;
662       s = pool->solvables + plist->elements[i];
663       if (s->obsoletes)
664         {
665           trj.firstidx = trj.nstack = trj.idx;
666           trj_visit(&trj, i);
667         }
668       else
669         {
670           Id myidx = trj.idx++;
671           trj.low[i] = myidx;
672           trj.stack[myidx] = i;
673         }
674     }
675   for (i = j = 0; i < plist->count; i++)
676     if (trj.low[i] >= 0)
677       plist->elements[j++] = plist->elements[i];
678   plist->count = j;
679   if (data != data_buf)
680     solv_free(data);
681 }
682
683 /* this is prune_obsoleted special-cased for two elements */
684 static void
685 prune_obsoleted_2(Pool *pool, Queue *plist)
686 {
687   int i;
688   Solvable *s;
689   Id p, pp, obs, *obsp;
690   Id other;
691   int obmap = 0;
692
693   for (i = 0; i < 2; i++)
694     {
695       s = pool->solvables + plist->elements[i];
696       other = plist->elements[1 - i];
697       if (s->obsoletes)
698         {
699           obsp = s->repo->idarraydata + s->obsoletes;
700           while ((obs = *obsp++) != 0)
701             {
702               FOR_PROVIDES(p, pp, obs)
703                 {
704                   Solvable *ps;
705                   if (p != other)
706                     continue;
707                   ps = pool->solvables + p;
708                   if (ps->name == s->name)
709                     continue;
710                   if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
711                     continue;
712                   if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
713                     continue;
714                   obmap |= 1 << i;
715                   break;
716                 }
717               if (p)
718                 break;
719             }
720         }
721     }
722   if (obmap == 0 || obmap == 3)
723     return;
724   if (obmap == 2)
725     plist->elements[0] = plist->elements[1];
726   plist->count = 1;
727 }
728
729 /*
730  * bring those elements to the front of the queue that
731  * have a installed solvable with the same name
732  */
733 static void
734 move_installed_to_front(Pool *pool, Queue *plist)
735 {
736   int i, j;
737   Solvable *s;
738   Id p, pp;
739
740   for (i = j = 0; i < plist->count; i++)
741     {
742       s = pool->solvables + plist->elements[i];
743       if (s->repo != pool->installed)
744         {
745           FOR_PROVIDES(p, pp, s->name)
746             {
747               Solvable *ps = pool->solvables + p;
748               if (s->name == ps->name && ps->repo == pool->installed)
749                 {
750                   s = ps;
751                   break;
752                 }
753             }
754         }
755       if (s->repo == pool->installed)
756         {
757           if (i != j)
758             {
759               p = plist->elements[i];
760               if (i - j == 1)
761                 plist->elements[i] = plist->elements[j];
762               else
763                 memmove(plist->elements + j + 1, plist->elements + j, (i - j) * sizeof(Id));
764               plist->elements[j] = p;
765             }
766           else if (j + 2 == plist->count)
767             break;      /* no need to check last element if all prev ones are installed */
768           j++;
769         }
770     }
771 }
772
773 /*
774  * prune_to_best_version
775  *
776  * sort list of packages (given through plist) by name and evr
777  * return result through plist
778  */
779 void
780 prune_to_best_version(Pool *pool, Queue *plist)
781 {
782   int i, j;
783   Solvable *s, *best;
784
785   if (plist->count < 2)         /* no need to prune for a single entry */
786     return;
787   POOL_DEBUG(SOLV_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
788
789   /* sort by name first, prefer installed */
790   solv_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool);
791
792   /* now find best 'per name' */
793   best = 0;
794   for (i = j = 0; i < plist->count; i++)
795     {
796       s = pool->solvables + plist->elements[i];
797
798       POOL_DEBUG(SOLV_DEBUG_POLICY, "- %s[%s]\n",
799                  pool_solvable2str(pool, s),
800                  (pool->installed && s->repo == pool->installed) ? "installed" : "not installed");
801
802       if (!best)                /* if no best yet, the current is best */
803         {
804           best = s;
805           continue;
806         }
807
808       /* name switch: finish group, re-init */
809       if (best->name != s->name)   /* new name */
810         {
811           plist->elements[j++] = best - pool->solvables; /* move old best to front */
812           best = s;             /* take current as new best */
813           continue;
814         }
815
816       if (best->evr != s->evr)  /* compare evr */
817         {
818           if (pool_evrcmp(pool, best->evr, s->evr, EVRCMP_COMPARE) < 0)
819             best = s;
820         }
821     }
822   plist->elements[j++] = best - pool->solvables;        /* finish last group */
823   plist->count = j;
824
825   /* we reduced the list to one package per name, now look at
826    * package obsoletes */
827   if (plist->count > 1)
828     {
829       if (plist->count == 2)
830         prune_obsoleted_2(pool, plist);
831       else
832         prune_obsoleted(pool, plist);
833     }
834   if (plist->count > 1 && pool->installed)
835     move_installed_to_front(pool, plist);
836 }
837
838
839 /*
840  *  POLICY_MODE_CHOOSE:     default, do all pruning steps
841  *  POLICY_MODE_RECOMMEND:  leave out prune_to_recommended
842  *  POLICY_MODE_SUGGEST:    leave out prune_to_recommended, do prio pruning just per name
843  */
844 void
845 policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
846 {
847   Pool *pool = solv->pool;
848   if (plist->count > 1)
849     {
850       if (mode != POLICY_MODE_SUGGEST)
851         solver_prune_to_highest_prio(solv, plist);
852       else
853         solver_prune_to_highest_prio_per_name(solv, plist);
854     }
855   if (plist->count > 1)
856     prune_to_best_arch(pool, plist);
857   if (plist->count > 1)
858     prune_to_best_version(pool, plist);
859   if (plist->count > 1 && mode == POLICY_MODE_CHOOSE)
860     prune_to_recommended(solv, plist);
861 }
862
863
864 /* check if there is an illegal architecture change if
865  * installed solvable s1 is replaced by s2 */
866 int
867 policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
868 {
869   Pool *pool = solv->pool;
870   Id a1 = s1->arch, a2 = s2->arch;
871
872   /* we allow changes to/from noarch */
873   if (a1 == a2 || a1 == pool->noarchid || a2 == pool->noarchid)
874     return 0;
875   if (!pool->id2arch)
876     return 0;
877   a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0;
878   a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0;
879   if (((a1 ^ a2) & 0xffff0000) != 0)
880     return 1;
881   return 0;
882 }
883
884 /* check if there is an illegal vendor change if
885  * installed solvable s1 is replaced by s2 */
886 int
887 policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
888 {
889   Pool *pool = solv->pool;
890   Id v1, v2;
891   Id vendormask1, vendormask2;
892
893   if (pool->custom_vendorcheck)
894      return pool->custom_vendorcheck(pool, s1, s2);
895
896   /* treat a missing vendor as empty string */
897   v1 = s1->vendor ? s1->vendor : ID_EMPTY;
898   v2 = s2->vendor ? s2->vendor : ID_EMPTY;
899   if (v1 == v2)
900     return 0;
901   vendormask1 = pool_vendor2mask(pool, v1);
902   if (!vendormask1)
903     return 1;   /* can't match */
904   vendormask2 = pool_vendor2mask(pool, v2);
905   if ((vendormask1 & vendormask2) != 0)
906     return 0;
907   return 1;     /* no class matches */
908 }
909
910 /* check if it is illegal to replace installed
911  * package "is" with package "s" (which must obsolete "is")
912  */
913 int
914 policy_is_illegal(Solver *solv, Solvable *is, Solvable *s, int ignore)
915 {
916   Pool *pool = solv->pool;
917   int ret = 0;
918   int duppkg = solv->dupmap_all ? 1 : 0;
919   if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !(duppkg ? solv->dup_allowdowngrade : solv->allowdowngrade))
920     {
921       if (is->name == s->name && pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0)
922         ret |= POLICY_ILLEGAL_DOWNGRADE;
923     }
924   if (!(ignore & POLICY_ILLEGAL_ARCHCHANGE) && !(duppkg ? solv->dup_allowarchchange : solv->allowarchchange))
925     {
926       if (is->arch != s->arch && policy_illegal_archchange(solv, is, s))
927         ret |= POLICY_ILLEGAL_ARCHCHANGE;
928     }
929   if (!(ignore & POLICY_ILLEGAL_VENDORCHANGE) && !(duppkg ? solv->dup_allowvendorchange : solv->allowvendorchange))
930     {
931       if (is->vendor != s->vendor && policy_illegal_vendorchange(solv, is, s))
932         ret |= POLICY_ILLEGAL_VENDORCHANGE;
933     }
934   if (!(ignore & POLICY_ILLEGAL_NAMECHANGE) && !(duppkg ? solv->dup_allownamechange : solv->allownamechange))
935     {
936       if (is->name != s->name)
937         ret |= POLICY_ILLEGAL_NAMECHANGE;
938     }
939   return ret;
940 }
941
942 /*-------------------------------------------------------------------
943  *
944  * create reverse obsoletes map for installed solvables
945  *
946  * For each installed solvable find which packages with *different* names
947  * obsolete the solvable.
948  * This index is used in policy_findupdatepackages() below.
949  */
950 void
951 policy_create_obsolete_index(Solver *solv)
952 {
953   Pool *pool = solv->pool;
954   Solvable *s;
955   Repo *installed = solv->installed;
956   Id p, pp, obs, *obsp, *obsoletes, *obsoletes_data;
957   int i, n, cnt;
958
959   solv->obsoletes = solv_free(solv->obsoletes);
960   solv->obsoletes_data = solv_free(solv->obsoletes_data);
961   if (!installed || installed->start == installed->end)
962     return;
963   cnt = installed->end - installed->start;
964   solv->obsoletes = obsoletes = solv_calloc(cnt, sizeof(Id));
965   for (i = 1; i < pool->nsolvables; i++)
966     {
967       s = pool->solvables + i;
968       if (!s->obsoletes)
969         continue;
970       if (!pool_installable(pool, s))
971         continue;
972       obsp = s->repo->idarraydata + s->obsoletes;
973       while ((obs = *obsp++) != 0)
974         {
975           FOR_PROVIDES(p, pp, obs)
976             {
977               Solvable *ps = pool->solvables + p;;
978               if (ps->repo != installed)
979                 continue;
980               if (ps->name == s->name)
981                 continue;
982               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
983                 continue;
984               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
985                 continue;
986               obsoletes[p - installed->start]++;
987             }
988         }
989     }
990   n = 0;
991   for (i = 0; i < cnt; i++)
992     if (obsoletes[i])
993       {
994         n += obsoletes[i] + 1;
995         obsoletes[i] = n;
996       }
997   solv->obsoletes_data = obsoletes_data = solv_calloc(n + 1, sizeof(Id));
998   POOL_DEBUG(SOLV_DEBUG_STATS, "obsoletes data: %d entries\n", n + 1);
999   for (i = pool->nsolvables - 1; i > 0; i--)
1000     {
1001       s = pool->solvables + i;
1002       if (!s->obsoletes)
1003         continue;
1004       if (!pool_installable(pool, s))
1005         continue;
1006       obsp = s->repo->idarraydata + s->obsoletes;
1007       while ((obs = *obsp++) != 0)
1008         {
1009           FOR_PROVIDES(p, pp, obs)
1010             {
1011               Solvable *ps = pool->solvables + p;;
1012               if (ps->repo != installed)
1013                 continue;
1014               if (ps->name == s->name)
1015                 continue;
1016               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
1017                 continue;
1018               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
1019                 continue;
1020               if (obsoletes_data[obsoletes[p - installed->start]] != i)
1021                 obsoletes_data[--obsoletes[p - installed->start]] = i;
1022             }
1023         }
1024     }
1025 }
1026
1027
1028 /*
1029  * find update candidates
1030  *
1031  * s: installed solvable to be updated
1032  * qs: [out] queue to hold Ids of candidates
1033  * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
1034  *            2 = dup mode
1035  *
1036  */
1037 void
1038 policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
1039 {
1040   /* installed packages get a special upgrade allowed rule */
1041   Pool *pool = solv->pool;
1042   Id p, pp, n, p2, pp2;
1043   Id obs, *obsp;
1044   Solvable *ps;
1045   int haveprovobs = 0;
1046   int allowdowngrade = allow_all ? 1 : solv->allowdowngrade;
1047   int allownamechange = allow_all ? 1 : solv->allownamechange;
1048   int allowarchchange = allow_all ? 1 : solv->allowarchchange;
1049   int allowvendorchange = allow_all ? 1 : solv->allowvendorchange;
1050   if (allow_all == 2)
1051     {
1052       allowdowngrade = solv->dup_allowdowngrade;
1053       allownamechange = solv->dup_allownamechange;
1054       allowarchchange = solv->dup_allowarchchange;
1055       allowvendorchange = solv->dup_allowvendorchange;
1056     }
1057
1058   queue_empty(qs);
1059
1060   n = s - pool->solvables;
1061
1062   /*
1063    * look for updates for s
1064    */
1065   FOR_PROVIDES(p, pp, s->name)  /* every provider of s' name */
1066     {
1067       if (p == n)               /* skip itself */
1068         continue;
1069
1070       ps = pool->solvables + p;
1071       if (s->name == ps->name)  /* name match */
1072         {
1073           if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
1074             continue;
1075           if (!allowdowngrade && pool_evrcmp(pool, s->evr, ps->evr, EVRCMP_COMPARE) > 0)
1076             continue;
1077         }
1078       else if (!allownamechange)
1079         continue;
1080       else if (!solv->noupdateprovide && ps->obsoletes)   /* provides/obsoletes combination ? */
1081         {
1082           /* check if package ps obsoletes installed package s */
1083           /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
1084            * use it to limit our update candidates */
1085           if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s, ps))
1086             continue;
1087           obsp = ps->repo->idarraydata + ps->obsoletes;
1088           while ((obs = *obsp++) != 0)  /* for all obsoletes */
1089             {
1090               FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
1091                 {
1092                   Solvable *ps2 = pool->solvables + p2;
1093                   if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps2, obs))
1094                     continue;
1095                   if (p2 == n)          /* match ! */
1096                     break;
1097                 }
1098               if (p2)                   /* match! */
1099                 break;
1100             }
1101           if (!obs)                     /* continue if no match */
1102             continue;
1103           /* here we have 'p' with a matching provides/obsoletes combination
1104            * thus flagging p as a valid update candidate for s
1105            */
1106           haveprovobs = 1;
1107         }
1108       else
1109         continue;
1110       if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
1111         continue;
1112       if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
1113         continue;
1114       queue_push(qs, p);
1115     }
1116   if (!allownamechange)
1117     return;
1118   /* if we have found some valid candidates and noupdateprovide is not set, we're
1119      done. otherwise we fallback to all obsoletes */
1120   if (!solv->noupdateprovide && haveprovobs)
1121     return;
1122   if (solv->obsoletes && solv->obsoletes[n - solv->installed->start])
1123     {
1124       Id *opp;
1125       for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;)
1126         {
1127           ps = pool->solvables + p;
1128           if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
1129             continue;
1130           if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
1131             continue;
1132           /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
1133            * use it to limit our update candidates */
1134           if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
1135             continue;
1136           queue_push(qs, p);
1137         }
1138     }
1139 }
1140
1141 const char *
1142 policy_illegal2str(Solver *solv, int illegal, Solvable *s, Solvable *rs)
1143 {
1144   Pool *pool = solv->pool;
1145   const char *str;
1146   if (illegal == POLICY_ILLEGAL_DOWNGRADE)
1147     {
1148       str = pool_tmpjoin(pool, "downgrade of ", pool_solvable2str(pool, s), 0);
1149       return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
1150     }
1151   if (illegal == POLICY_ILLEGAL_NAMECHANGE)
1152     {
1153       str = pool_tmpjoin(pool, "name change of ", pool_solvable2str(pool, s), 0);
1154       return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
1155     }
1156   if (illegal == POLICY_ILLEGAL_ARCHCHANGE)
1157     {
1158       str = pool_tmpjoin(pool, "architecture change of ", pool_solvable2str(pool, s), 0);
1159       return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
1160     }
1161   if (illegal == POLICY_ILLEGAL_VENDORCHANGE)
1162     {
1163       str = pool_tmpjoin(pool, "vendor change from '", pool_id2str(pool, s->vendor), "' (");
1164       if (rs->vendor)
1165         {
1166           str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to '");
1167           str = pool_tmpappend(pool, str, pool_id2str(pool, rs->vendor), "' (");
1168         }
1169       else
1170         str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to no vendor (");
1171       return pool_tmpappend(pool, str, pool_solvable2str(pool, rs), ")");
1172     }
1173   return "unknown illegal change";
1174 }
1175