93d744056c3229d2c6079121d725a30a58ba2d0c
[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 /* bring suggested/enhanced packages to front
410  * installed packages count as suggested */
411 static void
412 prefer_suggested(Solver *solv, Queue *plist)
413 {
414   Pool *pool = solv->pool;
415   int i, count;
416
417   /* update our recommendsmap/suggestsmap */
418   if (solv->recommends_index < solv->decisionq.count)
419     policy_update_recommendsmap(solv);
420
421   for (i = 0, count = plist->count; i < count; i++)
422     {
423       Id p = plist->elements[i];
424       Solvable *s = pool->solvables + p;
425       if ((pool->installed && s->repo == pool->installed) ||
426           MAPTST(&solv->suggestsmap, p) ||
427           solver_is_enhancing(solv, s))
428         continue;       /* good package */
429       /* bring to back */
430      if (i < plist->count - 1)
431         {
432           memmove(plist->elements + i, plist->elements + i + 1, (plist->count - 1 - i) * sizeof(Id));
433           plist->elements[plist->count - 1] = p;
434         }
435       i--;
436       count--;
437     }
438 }
439
440 /*
441  * prune to recommended/suggested packages.
442  * does not prune installed packages (they are also somewhat recommended).
443  */
444 static void
445 prune_to_recommended(Solver *solv, Queue *plist)
446 {
447   Pool *pool = solv->pool;
448   int i, j, k, ninst;
449   Solvable *s;
450   Id p;
451
452   ninst = 0;
453   if (pool->installed)
454     {
455       for (i = 0; i < plist->count; i++)
456         {
457           p = plist->elements[i];
458           s = pool->solvables + p;
459           if (pool->installed && s->repo == pool->installed)
460             ninst++;
461         }
462     }
463   if (plist->count - ninst < 2)
464     return;
465
466   /* update our recommendsmap/suggestsmap */
467   if (solv->recommends_index < solv->decisionq.count)
468     policy_update_recommendsmap(solv);
469
470   /* prune to recommended/supplemented */
471   ninst = 0;
472   for (i = j = 0; i < plist->count; i++)
473     {
474       p = plist->elements[i];
475       s = pool->solvables + p;
476       if (pool->installed && s->repo == pool->installed)
477         {
478           ninst++;
479           if (j)
480             plist->elements[j++] = p;
481           continue;
482         }
483       if (!MAPTST(&solv->recommendsmap, p))
484         if (!solver_is_supplementing(solv, s))
485           continue;
486       if (!j && ninst)
487         {
488           for (k = 0; j < ninst; k++)
489             {
490               s = pool->solvables + plist->elements[k];
491               if (pool->installed && s->repo == pool->installed)
492                 plist->elements[j++] = plist->elements[k];
493             }
494         }
495       plist->elements[j++] = p;
496     }
497   if (j)
498     plist->count = j;
499
500 #if 0
501   /* anything left to prune? */
502   if (plist->count - ninst < 2)
503     return;
504
505   /* prune to suggested/enhanced */
506   ninst = 0;
507   for (i = j = 0; i < plist->count; i++)
508     {
509       p = plist->elements[i];
510       s = pool->solvables + p;
511       if (pool->installed && s->repo == pool->installed)
512         {
513           ninst++;
514           if (j)
515             plist->elements[j++] = p;
516           continue;
517         }
518       if (!MAPTST(&solv->suggestsmap, p))
519         if (!solver_is_enhancing(solv, s))
520           continue;
521       if (!j && ninst)
522         {
523           for (k = 0; j < ninst; k++)
524             {
525               s = pool->solvables + plist->elements[k];
526               if (pool->installed && s->repo == pool->installed)
527                 plist->elements[j++] = plist->elements[k];
528             }
529         }
530       plist->elements[j++] = p;
531     }
532   if (j)
533     plist->count = j;
534 #endif
535 }
536
537 static void
538 prune_to_best_arch(const Pool *pool, Queue *plist)
539 {
540   Id a, bestscore;
541   Solvable *s;
542   int i, j;
543
544   if (!pool->id2arch || plist->count < 2)
545     return;
546   bestscore = 0;
547   for (i = 0; i < plist->count; i++)
548     {
549       s = pool->solvables + plist->elements[i];
550       a = s->arch;
551       a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
552       if (a && a != 1 && (!bestscore || a < bestscore))
553         bestscore = a;
554     }
555   if (!bestscore)
556     return;
557   for (i = j = 0; i < plist->count; i++)
558     {
559       s = pool->solvables + plist->elements[i];
560       a = s->arch;
561       if (a > pool->lastarch)
562         continue;
563       a = pool->id2arch[a];
564       /* a == 1 -> noarch */
565       if (a != 1 && ((a ^ bestscore) & 0xffff0000) != 0)
566         continue;
567       plist->elements[j++] = plist->elements[i];
568     }
569   if (j)
570     plist->count = j;
571 }
572
573
574 struct trj_data {
575   Pool *pool;
576   Queue *plist;
577   Id *stack;
578   Id nstack;
579   Id *low;
580   Id firstidx;
581   Id idx;
582 };
583
584 /* This is Tarjan's SCC algorithm, slightly modified */
585 static void
586 trj_visit(struct trj_data *trj, Id node)
587 {
588   Id *low = trj->low;
589   Pool *pool = trj->pool;
590   Queue *plist = trj->plist;
591   Id myidx, stackstart;
592   Solvable *s;
593   int i;
594   Id p, pp, obs, *obsp;
595
596   low[node] = myidx = trj->idx++;
597   trj->stack[(stackstart = trj->nstack++)] = node;
598
599   s = pool->solvables + plist->elements[node];
600   if (s->obsoletes)
601     {
602       obsp = s->repo->idarraydata + s->obsoletes;
603       while ((obs = *obsp++) != 0)
604         {
605           FOR_PROVIDES(p, pp, obs)
606             {
607               Solvable *ps = pool->solvables + p;
608               if (ps->name == s->name)
609                 continue;
610               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
611                 continue;
612               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
613                 continue;
614               /* hmm, expensive. should use hash if plist is big */
615               for (i = 0; i < plist->count; i++)
616                 {
617                   if (node != i && plist->elements[i] == p)
618                     {
619                       Id l = low[i];
620                       if (!l)
621                         {
622                           if (!ps->obsoletes)
623                             {
624                               /* don't bother */
625                               trj->idx++;
626                               low[i] = -1;
627                               continue;
628                             }
629                           trj_visit(trj, i);
630                           l = low[i];
631                         }
632                       if (l < 0)
633                         continue;
634                       if (l < trj->firstidx)
635                         {
636                           int k;
637                           /* this means we have reached an old SCC found earlier.
638                            * delete it as we obsolete it */
639                           for (k = l; ; k++)
640                             {
641                               if (low[trj->stack[k]] == l)
642                                 low[trj->stack[k]] = -1;
643                               else
644                                 break;
645                             }
646                         }
647                       else if (l < low[node])
648                         low[node] = l;
649                     }
650                 }
651             }
652         }
653     }
654   if (low[node] == myidx)       /* found a SCC? */
655     {
656       /* we're only interested in SCCs that contain the first node,
657        * as all others are "obsoleted" */
658       if (myidx != trj->firstidx)
659         myidx = -1;
660       for (i = stackstart; i < trj->nstack; i++)
661         low[trj->stack[i]] = myidx;
662       trj->nstack = stackstart; /* empty stack */
663     }
664 }
665
666 /*
667  * remove entries from plist that are obsoleted by other entries
668  * with different name.
669  */
670 static void
671 prune_obsoleted(Pool *pool, Queue *plist)
672 {
673   Id data_buf[2 * 16], *data;
674   struct trj_data trj;
675   int i, j;
676   Solvable *s;
677
678   if (plist->count <= 16)
679     {
680       memset(data_buf, 0, sizeof(data_buf));
681       data = data_buf;
682     }
683   else
684     data = solv_calloc(plist->count, 2 * sizeof(Id));
685   trj.pool = pool;
686   trj.plist = plist;
687   trj.low = data;
688   trj.idx = 1;
689   trj.stack = data + plist->count - 1;  /* -1 so we can index with idx (which starts with 1) */
690   for (i = 0; i < plist->count; i++)
691     {
692       if (trj.low[i])
693         continue;
694       s = pool->solvables + plist->elements[i];
695       if (s->obsoletes)
696         {
697           trj.firstidx = trj.nstack = trj.idx;
698           trj_visit(&trj, i);
699         }
700       else
701         {
702           Id myidx = trj.idx++;
703           trj.low[i] = myidx;
704           trj.stack[myidx] = i;
705         }
706     }
707   for (i = j = 0; i < plist->count; i++)
708     if (trj.low[i] >= 0)
709       plist->elements[j++] = plist->elements[i];
710   plist->count = j;
711   if (data != data_buf)
712     solv_free(data);
713 }
714
715 /* this is prune_obsoleted special-cased for two elements */
716 static void
717 prune_obsoleted_2(Pool *pool, Queue *plist)
718 {
719   int i;
720   Solvable *s;
721   Id p, pp, obs, *obsp;
722   Id other;
723   int obmap = 0;
724
725   for (i = 0; i < 2; i++)
726     {
727       s = pool->solvables + plist->elements[i];
728       other = plist->elements[1 - i];
729       if (s->obsoletes)
730         {
731           obsp = s->repo->idarraydata + s->obsoletes;
732           while ((obs = *obsp++) != 0)
733             {
734               FOR_PROVIDES(p, pp, obs)
735                 {
736                   Solvable *ps;
737                   if (p != other)
738                     continue;
739                   ps = pool->solvables + p;
740                   if (ps->name == s->name)
741                     continue;
742                   if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
743                     continue;
744                   if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
745                     continue;
746                   obmap |= 1 << i;
747                   break;
748                 }
749               if (p)
750                 break;
751             }
752         }
753     }
754   if (obmap == 0 || obmap == 3)
755     return;
756   if (obmap == 2)
757     plist->elements[0] = plist->elements[1];
758   plist->count = 1;
759 }
760
761 /*
762  * bring those elements to the front of the queue that
763  * have a installed solvable with the same name
764  */
765 static void
766 move_installed_to_front(Pool *pool, Queue *plist)
767 {
768   int i, j;
769   Solvable *s;
770   Id p, pp;
771
772   for (i = j = 0; i < plist->count; i++)
773     {
774       s = pool->solvables + plist->elements[i];
775       if (s->repo != pool->installed)
776         {
777           FOR_PROVIDES(p, pp, s->name)
778             {
779               Solvable *ps = pool->solvables + p;
780               if (s->name == ps->name && ps->repo == pool->installed)
781                 {
782                   s = ps;
783                   break;
784                 }
785             }
786         }
787       if (s->repo == pool->installed)
788         {
789           if (i != j)
790             {
791               p = plist->elements[i];
792               if (i - j == 1)
793                 plist->elements[i] = plist->elements[j];
794               else
795                 memmove(plist->elements + j + 1, plist->elements + j, (i - j) * sizeof(Id));
796               plist->elements[j] = p;
797             }
798           else if (j + 2 == plist->count)
799             break;      /* no need to check last element if all prev ones are installed */
800           j++;
801         }
802     }
803 }
804
805 /*
806  * prune_to_best_version
807  *
808  * sort list of packages (given through plist) by name and evr
809  * return result through plist
810  */
811 void
812 prune_to_best_version(Pool *pool, Queue *plist)
813 {
814   int i, j;
815   Solvable *s, *best;
816
817   if (plist->count < 2)         /* no need to prune for a single entry */
818     return;
819   POOL_DEBUG(SOLV_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
820
821   /* sort by name first, prefer installed */
822   solv_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool);
823
824   /* now find best 'per name' */
825   best = 0;
826   for (i = j = 0; i < plist->count; i++)
827     {
828       s = pool->solvables + plist->elements[i];
829
830       POOL_DEBUG(SOLV_DEBUG_POLICY, "- %s[%s]\n",
831                  pool_solvable2str(pool, s),
832                  (pool->installed && s->repo == pool->installed) ? "installed" : "not installed");
833
834       if (!best)                /* if no best yet, the current is best */
835         {
836           best = s;
837           continue;
838         }
839
840       /* name switch: finish group, re-init */
841       if (best->name != s->name)   /* new name */
842         {
843           plist->elements[j++] = best - pool->solvables; /* move old best to front */
844           best = s;             /* take current as new best */
845           continue;
846         }
847
848       if (best->evr != s->evr)  /* compare evr */
849         {
850           if (pool_evrcmp(pool, best->evr, s->evr, EVRCMP_COMPARE) < 0)
851             best = s;
852         }
853     }
854   plist->elements[j++] = best - pool->solvables;        /* finish last group */
855   plist->count = j;
856
857   /* we reduced the list to one package per name, now look at
858    * package obsoletes */
859   if (plist->count > 1)
860     {
861       if (plist->count == 2)
862         prune_obsoleted_2(pool, plist);
863       else
864         prune_obsoleted(pool, plist);
865     }
866   if (plist->count > 1 && pool->installed)
867     move_installed_to_front(pool, plist);
868 }
869
870
871 static int
872 sort_by_name_evr_sortcmp(const void *ap, const void *bp, void *dp)
873 {
874   Pool *pool = dp;
875   Id a, *aa = (Id *)ap;
876   Id b, *bb = (Id *)bp;
877   Id r = aa[1] - bb[1];
878   if (r)
879     return r < 0 ? -1 : 1;
880   a = aa[2] < 0 ? -aa[2] : aa[2];
881   b = bb[2] < 0 ? -bb[2] : bb[2];
882   if (pool->disttype != DISTTYPE_DEB)
883     {
884       /* treat release-less versions different */
885       const char *as = pool_id2str(pool, a);
886       const char *bs = pool_id2str(pool, b);
887       if (strchr(as, '-'))
888         {
889           if (!strchr(bs, '-'))
890             return -2;
891         }
892       else
893         {
894           if (strchr(bs, '-'))
895             return 2;
896         }
897     }
898   r = pool_evrcmp(pool, b, a, EVRCMP_COMPARE);
899   if (!r && (aa[2] < 0 || bb[2] < 0))
900     {
901       if (bb[2] >= 0)
902         return 1;
903       if (aa[2] >= 0)
904         return -1;
905     }
906   if (r)
907     return r < 0 ? -1 : 1;
908   return 0;
909 }
910
911 /* common end of sort_by_srcversion and sort_by_common_dep */
912 static void
913 sort_by_name_evr_array(Pool *pool, Queue *plist, int count, int ent)
914 {
915   Id lastname;
916   int i, j, bad, havebad;
917   Id *pp, *elements = plist->elements;
918
919   if (ent < 2)
920     {
921       queue_truncate(plist, count);
922       return;
923     }
924   solv_sort(elements + count * 2, ent, sizeof(Id) * 3, sort_by_name_evr_sortcmp, pool);
925   lastname = 0;
926   bad = havebad = 0;
927   for (i = 0, pp = elements + count * 2; i < ent; i++, pp += 3)
928     {
929       if (lastname && pp[1] == lastname)
930         {
931           if (pp[0] != pp[-3] && sort_by_name_evr_sortcmp(pp - 3, pp, pool) == -1)
932             {
933 #if 0
934               printf("%s - %s: bad %s %s - %s\n", pool_solvid2str(pool, elements[pp[-3]]), pool_solvid2str(pool, elements[pp[0]]), pool_dep2str(pool, lastname), pool_id2str(pool, pp[-1] < 0 ? -pp[-1] : pp[-1]), pool_id2str(pool, pp[2] < 0 ? -pp[2] : pp[2]));
935 #endif
936               bad++;
937               havebad = 1;
938             }
939         }
940       else
941         {
942           bad = 0;
943           lastname = pp[1];
944         }
945       elements[count + pp[0]] += bad;
946     }
947
948 #if 0
949 for (i = 0; i < count; i++)
950   printf("%s badness %d\n", pool_solvid2str(pool, elements[i]), elements[count + i]);
951 #endif
952
953   if (havebad)
954     {
955       /* simple stable insertion sort */
956       if (pool->installed)
957         for (i = 0; i < count; i++)
958           if (pool->solvables[elements[i]].repo == pool->installed)
959             elements[i + count] = 0;
960       for (i = 1; i < count; i++)
961         for (j = i, pp = elements + count + j; j > 0; j--, pp--)
962           if (pp[-1] > pp[0])
963             {
964               Id *pp2 = pp - count;
965               Id p = pp[-1];
966               pp[-1] = pp[0];
967               pp[0] = p;
968               p = pp2[-1];
969               pp2[-1] = pp2[0];
970               pp2[0] = p;
971             }
972           else
973             break;
974     }
975   queue_truncate(plist, count);
976 }
977
978 #if 0
979 static void
980 sort_by_srcversion(Pool *pool, Queue *plist)
981 {
982   int i, count = plist->count, ent = 0;
983   queue_insertn(plist, count, count, 0);
984   for (i = 0; i < count; i++)
985     {
986       Id name, evr, p = plist->elements[i];
987       Solvable *s = pool->solvables + p;
988       if (solvable_lookup_void(s, SOLVABLE_SOURCENAME))
989         name = s->name;
990       else
991         name = solvable_lookup_id(s, SOLVABLE_SOURCENAME);
992       if (solvable_lookup_void(s, SOLVABLE_SOURCEEVR))
993         evr = s->evr;
994       else
995         evr = solvable_lookup_id(s, SOLVABLE_SOURCEEVR);
996       if (!name || !evr || ISRELDEP(evr))
997         continue;
998       queue_push(plist, i);
999       queue_push2(plist, name, evr);
1000       ent++;
1001     }
1002   sort_by_name_evr_array(pool, plist, count, ent);
1003 }
1004 #endif
1005
1006 static void
1007 sort_by_common_dep(Pool *pool, Queue *plist)
1008 {
1009   int i, count = plist->count, ent = 0;
1010   Id id, *dp;
1011   queue_insertn(plist, count, count, 0);
1012   for (i = 0; i < count; i++)
1013     {
1014       Id p = plist->elements[i];
1015       Solvable *s = pool->solvables + p;
1016       if (!s->provides)
1017         continue;
1018       for (dp = s->repo->idarraydata + s->provides; (id = *dp++) != 0; )
1019         {
1020           Reldep *rd;
1021           if (!ISRELDEP(id))
1022             continue;
1023           rd = GETRELDEP(pool, id);
1024           if ((rd->flags == REL_EQ || rd->flags == (REL_EQ | REL_LT) || rd->flags == REL_LT) && !ISRELDEP(rd->evr))
1025             {
1026               if (rd->flags == REL_EQ)
1027                 {
1028                   /* ignore hashes */
1029                   const char *s = pool_id2str(pool, rd->evr);
1030                   if (strlen(s) >= 4)
1031                     {
1032                       while ((*s >= 'a' && *s <= 'f') || (*s >= '0' && *s <= '9'))
1033                         s++;
1034                       if (!*s)
1035                         continue;
1036                     }
1037                 }
1038               queue_push(plist, i);
1039               queue_push2(plist, rd->name, rd->flags == REL_LT ? -rd->evr : rd->evr);
1040               ent++;
1041             }
1042         }
1043     }
1044   sort_by_name_evr_array(pool, plist, count, ent);
1045 }
1046
1047 /* check if we have an update candidate */
1048 static void
1049 dislike_old_versions(Pool *pool, Queue *plist)
1050 {
1051   int i, count;
1052
1053   for (i = 0, count = plist->count; i < count; i++)
1054     {
1055       Id p = plist->elements[i];
1056       Solvable *s = pool->solvables + p;
1057       Repo *repo = s->repo;
1058       Id q, qq;
1059       int bad = 0;
1060
1061       if (!repo || repo == pool->installed)
1062         continue;
1063       FOR_PROVIDES(q, qq, s->name)
1064         {
1065           Solvable *qs = pool->solvables + q;
1066           if (q == p)
1067             continue;
1068           if (s->name != qs->name || s->arch != qs->arch)
1069             continue;
1070           if (repo->priority != qs->repo->priority)
1071             {
1072               if (repo->priority > qs->repo->priority)
1073                 continue;
1074               bad = 1;
1075               break;
1076             }
1077           if (pool_evrcmp(pool, qs->evr, s->evr, EVRCMP_COMPARE) > 0)
1078             {
1079               bad = 1;
1080               break;
1081             }
1082         }
1083       if (!bad)
1084         continue;
1085       /* bring to back */
1086       if (i < plist->count - 1)
1087         {
1088           memmove(plist->elements + i, plist->elements + i + 1, (plist->count - 1 - i) * sizeof(Id));
1089           plist->elements[plist->count - 1] = p;
1090         }
1091       i--;
1092       count--;
1093     }
1094 }
1095
1096 /*
1097  *  POLICY_MODE_CHOOSE:     default, do all pruning steps
1098  *  POLICY_MODE_RECOMMEND:  leave out prune_to_recommended
1099  *  POLICY_MODE_SUGGEST:    leave out prune_to_recommended, do prio pruning just per name
1100  */
1101 void
1102 policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
1103 {
1104   Pool *pool = solv->pool;
1105   if (plist->count > 1)
1106     {
1107       if (mode != POLICY_MODE_SUGGEST)
1108         solver_prune_to_highest_prio(solv, plist);
1109       else
1110         solver_prune_to_highest_prio_per_name(solv, plist);
1111     }
1112   if (plist->count > 1)
1113     prune_to_best_arch(pool, plist);
1114   if (plist->count > 1)
1115     prune_to_best_version(pool, plist);
1116   if (plist->count > 1 && mode == POLICY_MODE_CHOOSE)
1117     {
1118       prune_to_recommended(solv, plist);
1119       if (plist->count > 1)
1120         {
1121           /* do some fancy reordering */
1122 #if 0
1123           sort_by_srcversion(pool, plist);
1124 #endif
1125           dislike_old_versions(pool, plist);
1126           sort_by_common_dep(pool, plist);
1127           prefer_suggested(solv, plist);
1128         }
1129     }
1130 }
1131
1132
1133 /* check if there is an illegal architecture change if
1134  * installed solvable s1 is replaced by s2 */
1135 int
1136 policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
1137 {
1138   Pool *pool = solv->pool;
1139   Id a1 = s1->arch, a2 = s2->arch;
1140
1141   /* we allow changes to/from noarch */
1142   if (a1 == a2 || a1 == pool->noarchid || a2 == pool->noarchid)
1143     return 0;
1144   if (!pool->id2arch)
1145     return 0;
1146   a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0;
1147   a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0;
1148   if (((a1 ^ a2) & 0xffff0000) != 0)
1149     return 1;
1150   return 0;
1151 }
1152
1153 /* check if there is an illegal vendor change if
1154  * installed solvable s1 is replaced by s2 */
1155 int
1156 policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
1157 {
1158   Pool *pool = solv->pool;
1159   Id v1, v2;
1160   Id vendormask1, vendormask2;
1161
1162   if (pool->custom_vendorcheck)
1163      return pool->custom_vendorcheck(pool, s1, s2);
1164
1165   /* treat a missing vendor as empty string */
1166   v1 = s1->vendor ? s1->vendor : ID_EMPTY;
1167   v2 = s2->vendor ? s2->vendor : ID_EMPTY;
1168   if (v1 == v2)
1169     return 0;
1170   vendormask1 = pool_vendor2mask(pool, v1);
1171   if (!vendormask1)
1172     return 1;   /* can't match */
1173   vendormask2 = pool_vendor2mask(pool, v2);
1174   if ((vendormask1 & vendormask2) != 0)
1175     return 0;
1176   return 1;     /* no class matches */
1177 }
1178
1179 /* check if it is illegal to replace installed
1180  * package "is" with package "s" (which must obsolete "is")
1181  */
1182 int
1183 policy_is_illegal(Solver *solv, Solvable *is, Solvable *s, int ignore)
1184 {
1185   Pool *pool = solv->pool;
1186   int ret = 0;
1187   int duppkg = solv->dupmap_all ? 1 : 0;
1188   if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !(duppkg ? solv->dup_allowdowngrade : solv->allowdowngrade))
1189     {
1190       if (is->name == s->name && pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0)
1191         ret |= POLICY_ILLEGAL_DOWNGRADE;
1192     }
1193   if (!(ignore & POLICY_ILLEGAL_ARCHCHANGE) && !(duppkg ? solv->dup_allowarchchange : solv->allowarchchange))
1194     {
1195       if (is->arch != s->arch && policy_illegal_archchange(solv, is, s))
1196         ret |= POLICY_ILLEGAL_ARCHCHANGE;
1197     }
1198   if (!(ignore & POLICY_ILLEGAL_VENDORCHANGE) && !(duppkg ? solv->dup_allowvendorchange : solv->allowvendorchange))
1199     {
1200       if (is->vendor != s->vendor && policy_illegal_vendorchange(solv, is, s))
1201         ret |= POLICY_ILLEGAL_VENDORCHANGE;
1202     }
1203   if (!(ignore & POLICY_ILLEGAL_NAMECHANGE) && !(duppkg ? solv->dup_allownamechange : solv->allownamechange))
1204     {
1205       if (is->name != s->name)
1206         ret |= POLICY_ILLEGAL_NAMECHANGE;
1207     }
1208   return ret;
1209 }
1210
1211 /*-------------------------------------------------------------------
1212  *
1213  * create reverse obsoletes map for installed solvables
1214  *
1215  * For each installed solvable find which packages with *different* names
1216  * obsolete the solvable.
1217  * This index is used in policy_findupdatepackages() below.
1218  */
1219 void
1220 policy_create_obsolete_index(Solver *solv)
1221 {
1222   Pool *pool = solv->pool;
1223   Solvable *s;
1224   Repo *installed = solv->installed;
1225   Id p, pp, obs, *obsp, *obsoletes, *obsoletes_data;
1226   int i, n, cnt;
1227
1228   solv->obsoletes = solv_free(solv->obsoletes);
1229   solv->obsoletes_data = solv_free(solv->obsoletes_data);
1230   if (!installed || installed->start == installed->end)
1231     return;
1232   cnt = installed->end - installed->start;
1233   solv->obsoletes = obsoletes = solv_calloc(cnt, sizeof(Id));
1234   for (i = 1; i < pool->nsolvables; i++)
1235     {
1236       s = pool->solvables + i;
1237       if (!s->obsoletes)
1238         continue;
1239       if (!pool_installable(pool, s))
1240         continue;
1241       obsp = s->repo->idarraydata + s->obsoletes;
1242       while ((obs = *obsp++) != 0)
1243         {
1244           FOR_PROVIDES(p, pp, obs)
1245             {
1246               Solvable *ps = pool->solvables + p;;
1247               if (ps->repo != installed)
1248                 continue;
1249               if (ps->name == s->name)
1250                 continue;
1251               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
1252                 continue;
1253               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
1254                 continue;
1255               obsoletes[p - installed->start]++;
1256             }
1257         }
1258     }
1259   n = 0;
1260   for (i = 0; i < cnt; i++)
1261     if (obsoletes[i])
1262       {
1263         n += obsoletes[i] + 1;
1264         obsoletes[i] = n;
1265       }
1266   solv->obsoletes_data = obsoletes_data = solv_calloc(n + 1, sizeof(Id));
1267   POOL_DEBUG(SOLV_DEBUG_STATS, "obsoletes data: %d entries\n", n + 1);
1268   for (i = pool->nsolvables - 1; i > 0; i--)
1269     {
1270       s = pool->solvables + i;
1271       if (!s->obsoletes)
1272         continue;
1273       if (!pool_installable(pool, s))
1274         continue;
1275       obsp = s->repo->idarraydata + s->obsoletes;
1276       while ((obs = *obsp++) != 0)
1277         {
1278           FOR_PROVIDES(p, pp, obs)
1279             {
1280               Solvable *ps = pool->solvables + p;;
1281               if (ps->repo != installed)
1282                 continue;
1283               if (ps->name == s->name)
1284                 continue;
1285               if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
1286                 continue;
1287               if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
1288                 continue;
1289               if (obsoletes_data[obsoletes[p - installed->start]] != i)
1290                 obsoletes_data[--obsoletes[p - installed->start]] = i;
1291             }
1292         }
1293     }
1294 }
1295
1296
1297 /*
1298  * find update candidates
1299  *
1300  * s: installed solvable to be updated
1301  * qs: [out] queue to hold Ids of candidates
1302  * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
1303  *            2 = dup mode
1304  *
1305  */
1306 void
1307 policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
1308 {
1309   /* installed packages get a special upgrade allowed rule */
1310   Pool *pool = solv->pool;
1311   Id p, pp, n, p2, pp2;
1312   Id obs, *obsp;
1313   Solvable *ps;
1314   int haveprovobs = 0;
1315   int allowdowngrade = allow_all ? 1 : solv->allowdowngrade;
1316   int allownamechange = allow_all ? 1 : solv->allownamechange;
1317   int allowarchchange = allow_all ? 1 : solv->allowarchchange;
1318   int allowvendorchange = allow_all ? 1 : solv->allowvendorchange;
1319   if (allow_all == 2)
1320     {
1321       allowdowngrade = solv->dup_allowdowngrade;
1322       allownamechange = solv->dup_allownamechange;
1323       allowarchchange = solv->dup_allowarchchange;
1324       allowvendorchange = solv->dup_allowvendorchange;
1325     }
1326
1327   queue_empty(qs);
1328
1329   n = s - pool->solvables;
1330
1331   /*
1332    * look for updates for s
1333    */
1334   FOR_PROVIDES(p, pp, s->name)  /* every provider of s' name */
1335     {
1336       if (p == n)               /* skip itself */
1337         continue;
1338
1339       ps = pool->solvables + p;
1340       if (s->name == ps->name)  /* name match */
1341         {
1342           if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
1343             continue;
1344           if (!allowdowngrade && pool_evrcmp(pool, s->evr, ps->evr, EVRCMP_COMPARE) > 0)
1345             continue;
1346         }
1347       else if (!allownamechange)
1348         continue;
1349       else if (!solv->noupdateprovide && ps->obsoletes)   /* provides/obsoletes combination ? */
1350         {
1351           /* check if package ps obsoletes installed package s */
1352           /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
1353            * use it to limit our update candidates */
1354           if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s, ps))
1355             continue;
1356           obsp = ps->repo->idarraydata + ps->obsoletes;
1357           while ((obs = *obsp++) != 0)  /* for all obsoletes */
1358             {
1359               FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
1360                 {
1361                   Solvable *ps2 = pool->solvables + p2;
1362                   if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps2, obs))
1363                     continue;
1364                   if (p2 == n)          /* match ! */
1365                     break;
1366                 }
1367               if (p2)                   /* match! */
1368                 break;
1369             }
1370           if (!obs)                     /* continue if no match */
1371             continue;
1372           /* here we have 'p' with a matching provides/obsoletes combination
1373            * thus flagging p as a valid update candidate for s
1374            */
1375           haveprovobs = 1;
1376         }
1377       else
1378         continue;
1379       if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
1380         continue;
1381       if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
1382         continue;
1383       queue_push(qs, p);
1384     }
1385   if (!allownamechange)
1386     return;
1387   /* if we have found some valid candidates and noupdateprovide is not set, we're
1388      done. otherwise we fallback to all obsoletes */
1389   if (!solv->noupdateprovide && haveprovobs)
1390     return;
1391   if (solv->obsoletes && solv->obsoletes[n - solv->installed->start])
1392     {
1393       Id *opp;
1394       for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;)
1395         {
1396           ps = pool->solvables + p;
1397           if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
1398             continue;
1399           if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
1400             continue;
1401           /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless
1402            * use it to limit our update candidates */
1403           if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
1404             continue;
1405           queue_push(qs, p);
1406         }
1407     }
1408 }
1409
1410 const char *
1411 policy_illegal2str(Solver *solv, int illegal, Solvable *s, Solvable *rs)
1412 {
1413   Pool *pool = solv->pool;
1414   const char *str;
1415   if (illegal == POLICY_ILLEGAL_DOWNGRADE)
1416     {
1417       str = pool_tmpjoin(pool, "downgrade of ", pool_solvable2str(pool, s), 0);
1418       return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
1419     }
1420   if (illegal == POLICY_ILLEGAL_NAMECHANGE)
1421     {
1422       str = pool_tmpjoin(pool, "name change of ", pool_solvable2str(pool, s), 0);
1423       return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
1424     }
1425   if (illegal == POLICY_ILLEGAL_ARCHCHANGE)
1426     {
1427       str = pool_tmpjoin(pool, "architecture change of ", pool_solvable2str(pool, s), 0);
1428       return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs));
1429     }
1430   if (illegal == POLICY_ILLEGAL_VENDORCHANGE)
1431     {
1432       str = pool_tmpjoin(pool, "vendor change from '", pool_id2str(pool, s->vendor), "' (");
1433       if (rs->vendor)
1434         {
1435           str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to '");
1436           str = pool_tmpappend(pool, str, pool_id2str(pool, rs->vendor), "' (");
1437         }
1438       else
1439         str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to no vendor (");
1440       return pool_tmpappend(pool, str, pool_solvable2str(pool, rs), ")");
1441     }
1442   return "unknown illegal change";
1443 }
1444