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