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