Imported Upstream version 0.7.27
[platform/upstream/libsolv.git] / src / solvable.c
1 /*
2  * Copyright (c) 2008, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * solvable.c
10  *
11  * set/retrieve data from solvables
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdarg.h>
17 #include <unistd.h>
18 #include <string.h>
19
20 #include "pool.h"
21 #include "repo.h"
22 #include "util.h"
23 #include "policy.h"
24 #include "poolvendor.h"
25 #include "chksum.h"
26 #include "linkedpkg.h"
27 #include "evr.h"
28
29 const char *
30 pool_solvable2str(Pool *pool, Solvable *s)
31 {
32   const char *n, *e, *a;
33   int nl, el, al;
34   char *p;
35   n = pool_id2str(pool, s->name);
36   e = s->evr ? pool_id2str(pool, s->evr) : "";
37   /* XXX: may want to skip the epoch here */
38   a = s->arch ? pool_id2str(pool, s->arch) : "";
39   nl = strlen(n);
40   el = strlen(e);
41   al = strlen(a);
42   if (pool->havedistepoch)
43     {
44       /* strip the distepoch from the evr */
45       const char *de = strrchr(e, '-');
46       if (de && (de = strchr(de, ':')) != 0)
47         el = de - e;
48     }
49   p = pool_alloctmpspace(pool, nl + el + al + 3);
50   strcpy(p, n);
51   if (el)
52     {
53       p[nl++] = '-';
54       strncpy(p + nl, e, el);
55       p[nl + el] = 0;
56     }
57   if (al)
58     {
59       p[nl + el] = pool->disttype == DISTTYPE_HAIKU ? '-' : '.';
60       strcpy(p + nl + el + 1, a);
61     }
62   if (pool->disttype == DISTTYPE_CONDA && solvable_lookup_type(s, SOLVABLE_BUILDFLAVOR))
63     {
64       Queue flavorq;
65       int i;
66       queue_init(&flavorq);
67       solvable_lookup_idarray(s, SOLVABLE_BUILDFLAVOR, &flavorq);
68       for (i = 0; i < flavorq.count; i++)
69         p = pool_tmpappend(pool, p, "-", pool_id2str(pool, flavorq.elements[i]));
70       queue_free(&flavorq);
71     }
72   return p;
73 }
74
75 Id
76 solvable_lookup_type(Solvable *s, Id keyname)
77 {
78   if (!s->repo)
79     return 0;
80   return repo_lookup_type(s->repo, s - s->repo->pool->solvables, keyname);
81 }
82
83 Id
84 solvable_lookup_id(Solvable *s, Id keyname)
85 {
86   if (!s->repo)
87     return 0;
88   return repo_lookup_id(s->repo, s - s->repo->pool->solvables, keyname);
89 }
90
91 int
92 solvable_lookup_idarray(Solvable *s, Id keyname, Queue *q)
93 {
94   if (!s->repo)
95     {
96       queue_empty(q);
97       return 0;
98     }
99   return repo_lookup_idarray(s->repo, s - s->repo->pool->solvables, keyname, q);
100 }
101
102 int
103 solvable_lookup_deparray(Solvable *s, Id keyname, Queue *q, Id marker)
104 {
105   if (!s->repo)
106     {
107       queue_empty(q);
108       return 0;
109     }
110   return repo_lookup_deparray(s->repo, s - s->repo->pool->solvables, keyname, q, marker);
111 }
112
113 static const char *
114 solvable_lookup_str_joinarray(Solvable *s, Id keyname, const char *joinstr)
115 {
116   Queue q;
117   Id qbuf[10];
118   char *str = 0;
119
120   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
121   if (solvable_lookup_idarray(s, keyname, &q) && q.count)
122     {
123       Pool *pool = s->repo->pool;
124       if (q.count == 1)
125         str = (char *)pool_id2str(pool, q.elements[0]);
126       else
127         {
128           int i;
129           str = pool_tmpjoin(pool, pool_id2str(pool, q.elements[0]), 0, 0);
130           for (i = 1; i < q.count; i++)
131             str = pool_tmpappend(pool, str, joinstr, pool_id2str(pool, q.elements[i]));
132         }
133     }
134   queue_free(&q);
135   return str;
136 }
137
138 const char *
139 solvable_lookup_str(Solvable *s, Id keyname)
140 {
141   const char *str;
142   if (!s->repo)
143     return 0;
144   str = repo_lookup_str(s->repo, s - s->repo->pool->solvables, keyname);
145   if (!str && (keyname == SOLVABLE_LICENSE || keyname == SOLVABLE_GROUP || keyname == SOLVABLE_BUILDFLAVOR))
146     str = solvable_lookup_str_joinarray(s, keyname, ", ");
147   return str;
148 }
149
150 static const char *
151 solvable_lookup_str_base(Solvable *s, Id keyname, Id basekeyname, int usebase)
152 {
153   Pool *pool;
154   const char *str, *basestr;
155   Id p, pp, name;
156   Solvable *s2;
157   int pass;
158
159   if (!s->repo)
160     return 0;
161   pool = s->repo->pool;
162   str = solvable_lookup_str(s, keyname);
163   if (str || keyname == basekeyname)
164     return str;
165   basestr = solvable_lookup_str(s, basekeyname);
166   if (!basestr)
167     return 0;
168   /* search for a solvable with same name and same base that has the
169    * translation */
170   if (!pool->whatprovides)
171     return usebase ? basestr : 0;
172   name = s->name;
173   /* we do this in two passes, first same vendor, then all other vendors */
174   for (pass = 0; pass < 2; pass++)
175     {
176       FOR_PROVIDES(p, pp, name)
177         {
178           s2 = pool->solvables + p;
179           if (s2->name != name)
180             continue;
181           if ((s->vendor == s2->vendor) != (pass == 0))
182             continue;
183           str = solvable_lookup_str(s2, basekeyname);
184           if (!str || strcmp(str, basestr))
185             continue;
186           str = solvable_lookup_str(s2, keyname);
187           if (str)
188             return str;
189         }
190 #ifdef ENABLE_LINKED_PKGS
191       /* autopattern/product translation magic */
192       if (pass == 1 && name == s->name)
193         {
194           name = find_autopackage_name(pool, s);
195           if (name && name != s->name)
196             pass = -1;  /* start over with new name */
197         }
198 #endif
199     }
200   return usebase ? basestr : 0;
201 }
202
203 const char *
204 solvable_lookup_str_poollang(Solvable *s, Id keyname)
205 {
206   Pool *pool;
207   int i, cols;
208   const char *str;
209   Id *row;
210
211   if (!s->repo)
212     return 0;
213   pool = s->repo->pool;
214   if (!pool->nlanguages)
215     return solvable_lookup_str(s, keyname);
216   cols = pool->nlanguages + 1;
217   if (!pool->languagecache)
218     {
219       pool->languagecache = solv_calloc(cols * ID_NUM_INTERNAL, sizeof(Id));
220       pool->languagecacheother = 0;
221     }
222   if (keyname >= ID_NUM_INTERNAL)
223     {
224       row = pool->languagecache + ID_NUM_INTERNAL * cols;
225       for (i = 0; i < pool->languagecacheother; i++, row += cols)
226         if (*row == keyname)
227           break;
228       if (i >= pool->languagecacheother)
229         {
230           pool->languagecache = solv_realloc2(pool->languagecache, pool->languagecacheother + 1, cols * sizeof(Id));
231           row = pool->languagecache + cols * (ID_NUM_INTERNAL + pool->languagecacheother++);
232           *row = keyname;
233         }
234     }
235   else
236     row = pool->languagecache + keyname * cols;
237   row++;        /* skip keyname */
238   for (i = 0; i < pool->nlanguages; i++, row++)
239     {
240       if (!*row)
241         *row = pool_id2langid(pool, keyname, pool->languages[i], 1);
242       str = solvable_lookup_str_base(s, *row, keyname, 0);
243       if (str)
244         return str;
245     }
246   return solvable_lookup_str(s, keyname);
247 }
248
249 const char *
250 solvable_lookup_str_lang(Solvable *s, Id keyname, const char *lang, int usebase)
251 {
252   Id id;
253   if (!s->repo)
254     return 0;
255   id = pool_id2langid(s->repo->pool, keyname, lang, 0);
256   if (id)
257     return solvable_lookup_str_base(s, id, keyname, usebase);
258   if (!usebase)
259     return 0;
260   return solvable_lookup_str(s, keyname);
261 }
262
263 unsigned long long
264 solvable_lookup_num(Solvable *s, Id keyname, unsigned long long notfound)
265 {
266   if (!s->repo)
267     return notfound;
268   return repo_lookup_num(s->repo, s - s->repo->pool->solvables, keyname, notfound);
269 }
270
271 unsigned long long
272 solvable_lookup_sizek(Solvable *s, Id keyname, unsigned long long notfound)
273 {
274   unsigned long long size;
275   if (!s->repo)
276     return notfound;
277   size = solvable_lookup_num(s, keyname, (unsigned long long)-1);
278   return size == (unsigned long long)-1 ? notfound : ((size + 1023) >> 10);
279 }
280
281 int
282 solvable_lookup_void(Solvable *s, Id keyname)
283 {
284   if (!s->repo)
285     return 0;
286   return repo_lookup_void(s->repo, s - s->repo->pool->solvables, keyname);
287 }
288
289 int
290 solvable_lookup_bool(Solvable *s, Id keyname)
291 {
292   Id type;
293   if (!s->repo)
294     return 0;
295   /* historic nonsense: there are two ways of storing a bool, as num == 1 or void. test both. */
296   type = repo_lookup_type(s->repo, s - s->repo->pool->solvables, keyname);
297   if (type == REPOKEY_TYPE_VOID)
298     return 1;
299   if (type == REPOKEY_TYPE_NUM || type == REPOKEY_TYPE_CONSTANT)
300     return repo_lookup_num(s->repo, s - s->repo->pool->solvables, keyname, 0) == 1;
301   return 0;
302 }
303
304 const unsigned char *
305 solvable_lookup_bin_checksum(Solvable *s, Id keyname, Id *typep)
306 {
307   if (!s->repo)
308     {
309       *typep = 0;
310       return 0;
311     }
312   return repo_lookup_bin_checksum(s->repo, s - s->repo->pool->solvables, keyname, typep);
313 }
314
315 const char *
316 solvable_lookup_checksum(Solvable *s, Id keyname, Id *typep)
317 {
318   const unsigned char *chk = solvable_lookup_bin_checksum(s, keyname, typep);
319   return chk ? pool_bin2hex(s->repo->pool, chk, solv_chksum_len(*typep)) : 0;
320 }
321
322 unsigned int
323 solvable_lookup_count(Solvable *s, Id keyname)
324 {
325   return s->repo ? repo_lookup_count(s->repo, s - s->repo->pool->solvables, keyname) : 0;
326 }
327
328 static inline const char *
329 evrid2vrstr(Pool *pool, Id evrid)
330 {
331   const char *p, *evr = pool_id2str(pool, evrid);
332   if (!evr)
333     return evr;
334   for (p = evr; *p >= '0' && *p <= '9'; p++)
335     ;
336   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
337 }
338
339 const char *
340 solvable_lookup_location(Solvable *s, unsigned int *medianrp)
341 {
342   Pool *pool;
343   int l = 0;
344   char *loc;
345   const char *mediadir, *mediafile;
346
347   if (medianrp)
348     *medianrp = 0;
349   if (!s->repo)
350     return 0;
351   pool = s->repo->pool;
352   if (medianrp)
353     *medianrp = solvable_lookup_num(s, SOLVABLE_MEDIANR, 0);
354   if (solvable_lookup_void(s, SOLVABLE_MEDIADIR))
355     mediadir = pool_id2str(pool, s->arch);
356   else
357     mediadir = solvable_lookup_str(s, SOLVABLE_MEDIADIR);
358   if (mediadir)
359     l = strlen(mediadir) + 1;
360   if (solvable_lookup_void(s, SOLVABLE_MEDIAFILE))
361     {
362       const char *name, *evr, *arch;
363       name = pool_id2str(pool, s->name);
364       evr = evrid2vrstr(pool, s->evr);
365       arch = pool_id2str(pool, s->arch);
366       /* name-vr.arch.rpm */
367       loc = pool_alloctmpspace(pool, l + strlen(name) + strlen(evr) + strlen(arch) + 7);
368       if (mediadir)
369         sprintf(loc, "%s/%s-%s.%s.rpm", mediadir, name, evr, arch);
370       else
371         sprintf(loc, "%s-%s.%s.rpm", name, evr, arch);
372     }
373   else
374     {
375       mediafile = solvable_lookup_str(s, SOLVABLE_MEDIAFILE);
376       if (!mediafile)
377         return 0;
378       loc = pool_alloctmpspace(pool, l + strlen(mediafile) + 1);
379       if (mediadir)
380         sprintf(loc, "%s/%s", mediadir, mediafile);
381       else
382         strcpy(loc, mediafile);
383     }
384   return loc;
385 }
386
387 const char *
388 solvable_get_location(Solvable *s, unsigned int *medianrp)
389 {
390   const char *loc = solvable_lookup_location(s, medianrp);
391   if (medianrp && *medianrp == 0)
392     *medianrp = 1;      /* compat, to be removed */
393   return loc;
394 }
395
396 const char *
397 solvable_lookup_sourcepkg(Solvable *s)
398 {
399   Pool *pool;
400   const char *evr, *name;
401   Id archid;
402
403   if (!s->repo)
404     return 0;
405   pool = s->repo->pool;
406   if (solvable_lookup_void(s, SOLVABLE_SOURCENAME))
407     name = pool_id2str(pool, s->name);
408   else
409     name = solvable_lookup_str(s, SOLVABLE_SOURCENAME);
410   if (!name)
411     return 0;
412   archid = solvable_lookup_id(s, SOLVABLE_SOURCEARCH);
413   if (solvable_lookup_void(s, SOLVABLE_SOURCEEVR))
414     evr = evrid2vrstr(pool, s->evr);
415   else
416     evr = solvable_lookup_str(s, SOLVABLE_SOURCEEVR);
417   if (archid == ARCH_SRC || archid == ARCH_NOSRC)
418     {
419       char *str;
420       str = pool_tmpjoin(pool, name, evr ? "-" : 0, evr);
421       str = pool_tmpappend(pool, str, ".", pool_id2str(pool, archid));
422       return pool_tmpappend(pool, str, ".rpm", 0);
423     }
424   else
425     return name;        /* FIXME */
426 }
427
428
429 /*****************************************************************************/
430
431 /*
432  * Create maps containing the state of each solvable. Input is a "installed" queue,
433  * it contains all solvable ids that are considered to be installed.
434  *
435  * The created maps can be used for * pool_calc_duchanges() and
436  * pool_calc_installsizechange().
437  *
438  */
439 void
440 pool_create_state_maps(Pool *pool, Queue *installed, Map *installedmap, Map *conflictsmap)
441 {
442   int i;
443   Solvable *s;
444   Id p, *dp;
445   Id *conp, con;
446
447   map_init(installedmap, pool->nsolvables);
448   if (conflictsmap)
449     map_init(conflictsmap, pool->nsolvables);
450   for (i = 0; i < installed->count; i++)
451     {
452       p = installed->elements[i];
453       if (p <= 0)       /* makes it work with decisionq */
454         continue;
455       MAPSET(installedmap, p);
456       if (!conflictsmap)
457         continue;
458       s = pool->solvables + p;
459       if (!s->conflicts)
460         continue;
461       conp = s->repo->idarraydata + s->conflicts;
462       while ((con = *conp++) != 0)
463         {
464           dp = pool_whatprovides_ptr(pool, con);
465           for (; *dp; dp++)
466             MAPSET(conflictsmap, *dp);
467         }
468     }
469 }
470
471 /* Tests if two solvables have identical content. Currently
472  * both solvables need to come from the same pool
473  */
474
475 int
476 solvable_identical(Solvable *s1, Solvable *s2)
477 {
478   unsigned long long bt1, bt2;
479   Id rq1, rq2;
480   Id *reqp;
481   if (s1->name != s2->name)
482     return 0;
483   if (s1->arch != s2->arch)
484     return 0;
485   if (s1->evr != s2->evr)
486     return 0;
487
488   /* check vendor, map missing vendor to empty string */
489   if ((s1->vendor ? s1->vendor : 1) != (s2->vendor ? s2->vendor : 1))
490     {
491       /* workaround for bug 881493 */
492       if (s1->repo && !strncmp(pool_id2str(s1->repo->pool, s1->name), "product:", 8))
493         return 1;
494       return 0;
495     }
496
497   /* looking good, try some fancier stuff */
498   /* might also look up the package checksum here */
499   bt1 = solvable_lookup_num(s1, SOLVABLE_BUILDTIME, 0);
500   bt2 = solvable_lookup_num(s2, SOLVABLE_BUILDTIME, 0);
501   if (bt1 && bt2)
502     {
503       if (bt1 != bt2)
504         return 0;
505     }
506   else
507     {
508       if (s1->repo)
509         {
510           /* workaround for bugs 881493 and 885830*/
511           const char *n = pool_id2str(s1->repo->pool, s1->name);
512           if (!strncmp(n, "product:", 8) || !strncmp(n, "application:", 12))
513             return 1;
514         }
515       /* look at requires in a last attempt to find recompiled packages */
516       rq1 = rq2 = 0;
517       if (s1->requires)
518         for (reqp = s1->repo->idarraydata + s1->requires; *reqp; reqp++)
519           rq1 ^= *reqp;
520       if (s2->requires)
521         for (reqp = s2->repo->idarraydata + s2->requires; *reqp; reqp++)
522           rq2 ^= *reqp;
523       if (rq1 != rq2)
524          return 0;
525     }
526   if (s1->repo && s1->repo->pool->disttype == DISTTYPE_CONDA)
527     {
528       /* check buildflavor and buildversion */
529       const char *str1, *str2;
530       str1 = solvable_lookup_str(s1, SOLVABLE_BUILDFLAVOR);
531       str2 = solvable_lookup_str(s2, SOLVABLE_BUILDFLAVOR);
532       if (str1 != str2 && (!str1 || !str2 || strcmp(str1, str2) != 0))
533         return 0;
534       str1 = solvable_lookup_str(s1, SOLVABLE_BUILDVERSION);
535       str2 = solvable_lookup_str(s2, SOLVABLE_BUILDVERSION);
536       if (str1 != str2 && (!str1 || !str2 || strcmp(str1, str2) != 0))
537         return 0;
538     }
539   return 1;
540 }
541
542 /* return the self provide dependency of a solvable */
543 Id
544 solvable_selfprovidedep(Solvable *s)
545 {
546   Pool *pool;
547   Reldep *rd;
548   Id prov, *provp;
549
550   if (!s->repo)
551     return s->name;
552   pool = s->repo->pool;
553   if (s->provides)
554     {
555       provp = s->repo->idarraydata + s->provides;
556       while ((prov = *provp++) != 0)
557         {
558           if (!ISRELDEP(prov))
559             continue;
560           rd = GETRELDEP(pool, prov);
561           if (rd->name == s->name && rd->evr == s->evr && rd->flags == REL_EQ)
562             return prov;
563         }
564     }
565   return pool_rel2id(pool, s->name, s->evr, REL_EQ, 1);
566 }
567
568 /* setter functions, simply call the repo variants */
569 void
570 solvable_set_id(Solvable *s, Id keyname, Id id)
571 {
572   repo_set_id(s->repo, s - s->repo->pool->solvables, keyname, id);
573 }
574
575 void
576 solvable_set_num(Solvable *s, Id keyname, unsigned long long num)
577 {
578   repo_set_num(s->repo, s - s->repo->pool->solvables, keyname, num);
579 }
580
581 void
582 solvable_set_str(Solvable *s, Id keyname, const char *str)
583 {
584   repo_set_str(s->repo, s - s->repo->pool->solvables, keyname, str);
585 }
586
587 void
588 solvable_set_poolstr(Solvable *s, Id keyname, const char *str)
589 {
590   repo_set_poolstr(s->repo, s - s->repo->pool->solvables, keyname, str);
591 }
592
593 void
594 solvable_add_poolstr_array(Solvable *s, Id keyname, const char *str)
595 {
596   repo_add_poolstr_array(s->repo, s - s->repo->pool->solvables, keyname, str);
597 }
598
599 void
600 solvable_add_idarray(Solvable *s, Id keyname, Id id)
601 {
602   repo_add_idarray(s->repo, s - s->repo->pool->solvables, keyname, id);
603 }
604
605 void
606 solvable_add_deparray(Solvable *s, Id keyname, Id dep, Id marker)
607 {
608   repo_add_deparray(s->repo, s - s->repo->pool->solvables, keyname, dep, marker);
609 }
610
611 void
612 solvable_set_idarray(Solvable *s, Id keyname, Queue *q)
613 {
614   repo_set_idarray(s->repo, s - s->repo->pool->solvables, keyname, q);
615 }
616
617 void
618 solvable_set_deparray(Solvable *s, Id keyname, Queue *q, Id marker)
619 {
620   repo_set_deparray(s->repo, s - s->repo->pool->solvables, keyname, q, marker);
621 }
622
623 void
624 solvable_unset(Solvable *s, Id keyname)
625 {
626   repo_unset(s->repo, s - s->repo->pool->solvables, keyname);
627 }
628
629 /* return true if a dependency intersects dep in the keyname array */
630 int
631 solvable_matchesdep(Solvable *s, Id keyname, Id dep, int marker)
632 {
633   int i;
634   Pool *pool = s->repo->pool;
635   Queue q;
636
637   if (keyname == SOLVABLE_NAME)
638     return pool_match_nevr(pool, s, dep) ? 1 : 0;       /* nevr match hack */
639   queue_init(&q);
640   solvable_lookup_deparray(s, keyname, &q, marker);
641   for (i = 0; i < q.count; i++)
642     if (pool_match_dep(pool, q.elements[i], dep))
643       break;
644   i = i == q.count ? 0 : 1;
645   queue_free(&q);
646   return i;
647 }
648
649 int
650 solvable_matchessolvable_int(Solvable *s, Id keyname, int marker, Id solvid, Map *solvidmap, Queue *depq, Map *missc, int reloff, Queue *outdepq)
651 {
652   Pool *pool = s->repo->pool;
653   int i, boff;
654   Id *wp;
655
656   if (depq->count)
657     queue_empty(depq);
658   if (outdepq && outdepq->count)
659     queue_empty(outdepq);
660   solvable_lookup_deparray(s, keyname, depq, marker);
661   for (i = 0; i < depq->count; i++)
662     {
663       Id dep = depq->elements[i];
664       boff = ISRELDEP(dep) ? reloff + GETRELID(dep) : dep;
665       if (MAPTST(missc, boff))
666         continue;
667       if (ISRELDEP(dep))
668         {
669           Reldep *rd = GETRELDEP(pool, dep);
670           if (!ISRELDEP(rd->name) && rd->flags < 8)
671             {
672               /* do pre-filtering on the base */
673               if (MAPTST(missc, rd->name))
674                 continue;
675               wp = pool_whatprovides_ptr(pool, rd->name);
676               if (solvidmap)
677                 {
678                   for (; *wp; wp++)
679                     if (MAPTST(solvidmap, *wp))
680                       break;
681                 }
682               else
683                 {
684                   for (; *wp; wp++)
685                     if (*wp == solvid)
686                       break;
687                 }
688               if (!*wp)
689                 {
690                   /* the base does not include solvid, no need to check the complete dep */
691                   MAPSET(missc, rd->name);
692                   MAPSET(missc, boff);
693                   continue;
694                 }
695             }
696         }
697       wp = pool_whatprovides_ptr(pool, dep);
698       if (solvidmap)
699         {
700           for (; *wp; wp++)
701             if (MAPTST(solvidmap, *wp))
702               break;
703         }
704       else
705         {
706           for (; *wp; wp++)
707             if (*wp == solvid)
708               break;
709         }
710       if (*wp)
711         {
712           if (outdepq)
713             {
714               queue_pushunique(outdepq, dep);
715               continue;
716             }
717           return 1;
718         }
719       MAPSET(missc, boff);
720     }
721   return outdepq && outdepq->count ? 1 : 0;
722 }
723
724 int
725 solvable_matchessolvable(Solvable *s, Id keyname, Id solvid, Queue *depq, int marker)
726 {
727   Pool *pool = s->repo->pool;
728   Map missc;            /* cache for misses */
729   int res, reloff;
730   Queue qq;
731
732   if (depq && depq->count)
733     queue_empty(depq);
734   if (s - pool->solvables == solvid)
735     return 0;           /* no self-matches */
736
737   queue_init(&qq);
738   reloff = pool->ss.nstrings;
739   map_init(&missc, reloff + pool->nrels);
740   res = solvable_matchessolvable_int(s, keyname, marker, solvid, 0, &qq, &missc, reloff, depq);
741   map_free(&missc);
742   queue_free(&qq);
743   return res;
744 }
745
746 static int
747 solvidset2str_evrcmp(Pool *pool, Id a, Id b)
748 {
749   Solvable *as = pool->solvables + a, *bs = pool->solvables + b;
750   return as->evr != bs->evr ? pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE) : 0;
751 }
752
753 static int
754 solvidset2str_sortcmp(const void *va, const void *vb, void *vd)
755 {
756   Pool *pool = vd;
757   Solvable *as = pool->solvables + *(Id *)va, *bs = pool->solvables + *(Id *)vb;
758   if (as->name != bs->name)
759     {
760       int r = strcmp(pool_id2str(pool, as->name), pool_id2str(pool, bs->name));
761       if (r)
762         return r;
763       return as->name - bs->name;
764     }
765   if (as->evr != bs->evr)
766     {
767       int r = pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE);
768       if (r)
769         return r;
770     }
771   return *(Id *)va - *(Id *)vb;
772 }
773
774 static const char *
775 solvidset2str_striprelease(Pool *pool, Id evr, Id otherevr)
776 {
777   const char *evrstr = pool_id2str(pool, evr);
778   const char *r = strchr(evrstr, '-');
779   char *evrstr2;
780   int cmp;
781   if (!r)
782     return evrstr;
783   evrstr2 = pool_tmpjoin(pool, evrstr, 0, 0);
784   evrstr2[r - evrstr] = 0;
785   cmp = pool_evrcmp_str(pool, evrstr2, pool_id2str(pool, otherevr), pool->disttype != DISTTYPE_DEB ? EVRCMP_MATCH_RELEASE : EVRCMP_COMPARE);
786   return cmp == 1 ? evrstr2 : evrstr;
787 }
788
789 const char *
790 pool_solvidset2str(Pool *pool, Queue *q)
791 {
792   Queue pq;
793   Queue pr;
794   char *s = 0;
795   int i, j, k, kstart;
796   Id name = 0;
797
798   if (!q->count)
799     return "";
800   if (q->count == 1)
801     return pool_solvid2str(pool, q->elements[0]);
802   queue_init_clone(&pq, q);
803   queue_init(&pr);
804   solv_sort(pq.elements, pq.count, sizeof(Id), solvidset2str_sortcmp, pool);
805
806   for (i = 0; i < pq.count; i++)
807     {
808       Id p = pq.elements[i];
809       if (s)
810         s = pool_tmpappend(pool, s, ", ", 0);
811
812       if (i == 0 || pool->solvables[p].name != name)
813         {
814           Id p2, pp2;
815           name = pool->solvables[p].name;
816           queue_empty(&pr);
817           FOR_PROVIDES(p2, pp2, name)
818             if (pool->solvables[p].name == name)
819               queue_push(&pr, p2);
820           if (pr.count > 1)
821             solv_sort(pr.elements, pr.count, sizeof(Id), solvidset2str_sortcmp, pool);
822         }
823
824       for (k = 0; k < pr.count; k++)
825         if (pr.elements[k] == p)
826           break;
827       if (k == pr.count)
828         {
829           /* not in provides, list as singularity */
830           s = pool_tmpappend(pool, s, pool_solvid2str(pool, pq.elements[i]), 0);
831           continue;
832         }
833       if (k && solvidset2str_evrcmp(pool, pr.elements[k], pr.elements[k - 1]) == 0)
834         {
835           /* unclear start, list as single package */
836           s = pool_tmpappend(pool, s, pool_solvid2str(pool, pq.elements[i]), 0);
837           continue;
838         }
839       kstart = k;
840       for (j = i + 1, k = k + 1; j < pq.count; j++, k++)
841         if (k == pr.count || pq.elements[j] != pr.elements[k])
842           break;
843       while (j > i + 1 && k && k < pr.count && solvidset2str_evrcmp(pool, pr.elements[k], pr.elements[k - 1]) == 0)
844         {
845           j--;
846           k--;
847         }
848       if (k == 0 || j == i + 1)
849         {
850           s = pool_tmpappend(pool, s, pool_solvid2str(pool, pq.elements[i]), 0);
851           continue;
852         }
853       /* create an interval */
854       s = pool_tmpappend(pool, s, pool_id2str(pool, name), 0);
855       if (kstart > 0)
856         s = pool_tmpappend(pool, s, " >= ", solvidset2str_striprelease(pool, pool->solvables[pr.elements[kstart]].evr, pool->solvables[pr.elements[kstart - 1]].evr));
857       if (k < pr.count)
858         s = pool_tmpappend(pool, s, " < ", solvidset2str_striprelease(pool, pool->solvables[pr.elements[k]].evr, pool->solvables[pr.elements[k - 1]].evr));
859       i = j - 1;
860     }
861   queue_free(&pq);
862   queue_free(&pr);
863   return s;
864 }
865