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