- update transaction ordering code
[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
24 const char *
25 solvable2str(Pool *pool, Solvable *s)
26 {
27   const char *n, *e, *a;
28   char *p;
29   n = id2str(pool, s->name);
30   e = id2str(pool, s->evr);
31   a = id2str(pool, s->arch);
32   p = pool_alloctmpspace(pool, strlen(n) + strlen(e) + strlen(a) + 3);
33   sprintf(p, "%s-%s.%s", n, e, a);
34   return p;
35 }
36
37 Id
38 solvable_lookup_id(Solvable *s, Id keyname)
39 {
40   if (!s->repo)
41     return 0;
42   return repo_lookup_id(s->repo, s - s->repo->pool->solvables, keyname);
43 }
44
45 int
46 solvable_lookup_idarray(Solvable *s, Id keyname, Queue *q)
47 {
48   Dataiterator di;
49   int found = 0;
50
51   queue_empty(q);
52   if (!s->repo)
53     return 0;
54   dataiterator_init(&di, s->repo->pool, s->repo, s - s->repo->pool->solvables, keyname, 0, SEARCH_ARRAYSENTINEL);
55   while (dataiterator_step(&di))
56     {
57       if (di.key->type != REPOKEY_TYPE_IDARRAY && di.key->type != REPOKEY_TYPE_REL_IDARRAY)
58         continue;
59       found = 1;
60       if (di.kv.eof)
61         break;
62       queue_push(q, di.kv.id);
63     }
64   dataiterator_free(&di);
65   return found;
66 }
67
68 const char *
69 solvable_lookup_str(Solvable *s, Id keyname)
70 {
71   if (!s->repo)
72     return 0;
73   return repo_lookup_str(s->repo, s - s->repo->pool->solvables, keyname);
74 }
75
76 const char *
77 solvable_lookup_str_poollang(Solvable *s, Id keyname)
78 {
79   Pool *pool;
80   int i, cols;
81   const char *str;
82   Id *row;
83
84   if (!s->repo)
85     return 0;
86   pool = s->repo->pool;
87   if (!pool->nlanguages)
88     return solvable_lookup_str(s, keyname);
89   cols = pool->nlanguages + 1;
90   if (!pool->languagecache)
91     {
92       pool->languagecache = sat_calloc(cols * ID_NUM_INTERNAL, sizeof(Id));
93       pool->languagecacheother = 0;
94     }
95   if (keyname >= ID_NUM_INTERNAL)
96     {
97       row = pool->languagecache + ID_NUM_INTERNAL * cols;
98       for (i = 0; i < pool->languagecacheother; i++, row += cols)
99         if (*row == keyname)
100           break;
101       if (i >= pool->languagecacheother)
102         {
103           pool->languagecache = sat_realloc2(pool->languagecache, pool->languagecacheother + 1, cols * sizeof(Id));
104           pool->languagecacheother++;
105           row = pool->languagecache + cols * (ID_NUM_INTERNAL + pool->languagecacheother++);
106         }
107     }
108   else
109     row = pool->languagecache + keyname * cols;
110   row++;        /* skip keyname */
111   for (i = 0; i < pool->nlanguages; i++, row++)
112     {
113       if (!*row)
114         {
115           char *p;
116           const char *kn;
117
118           kn = id2str(pool, keyname);
119           p = sat_malloc(strlen(kn) + strlen(pool->languages[i]) + 2);
120           sprintf(p, "%s:%s", kn, pool->languages[i]);
121           *row = str2id(pool, p, 1);
122           sat_free(p);
123         }
124       str = solvable_lookup_str(s, *row);
125       if (str)
126         return str;
127     }
128   return solvable_lookup_str(s, keyname);
129 }
130
131 const char *
132 solvable_lookup_str_lang(Solvable *s, Id keyname, const char *lang)
133 {
134   if (s->repo)
135     {
136       const char *str;
137       Id id = pool_id2langid(s->repo->pool, keyname, lang, 0);
138       if (id && (str = solvable_lookup_str(s, id)) != 0)
139         return str;
140     }
141   return solvable_lookup_str(s, keyname);
142 }
143
144 unsigned int
145 solvable_lookup_num(Solvable *s, Id keyname, unsigned int notfound)
146 {
147   if (!s->repo)
148     return 0;
149   return repo_lookup_num(s->repo, s - s->repo->pool->solvables, keyname, notfound);
150 }
151
152 int
153 solvable_lookup_void(Solvable *s, Id keyname)
154 {
155   if (!s->repo)
156     return 0;
157   return repo_lookup_void(s->repo, s - s->repo->pool->solvables, keyname);
158 }
159
160 int
161 solvable_lookup_bool(Solvable *s, Id keyname)
162 {
163   Repo *repo = s->repo;
164   Pool *pool;
165   Repodata *data;
166   int i, j, n;
167
168   if (!repo)
169     return 0;
170   pool = repo->pool;
171   n = s - pool->solvables;
172   for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
173     {
174       if (n < data->start || n >= data->end)
175         continue;
176       /* there are two ways of storing a bool, as num == 1 or void */
177       for (j = 1; j < data->nkeys; j++)
178         {
179           if (data->keys[j].name == keyname
180               && (data->keys[j].type == REPOKEY_TYPE_U32
181                   || data->keys[j].type == REPOKEY_TYPE_NUM
182                   || data->keys[j].type == REPOKEY_TYPE_CONSTANT
183                   || data->keys[j].type == REPOKEY_TYPE_VOID))
184             {
185               unsigned int value;
186               if (repodata_lookup_num(data, n, keyname, &value))
187                 return value == 1;
188               if (repodata_lookup_void(data, n, keyname))
189                 return 1;
190             }
191         }
192     }
193   return 0;
194 }
195
196 const unsigned char *
197 solvable_lookup_bin_checksum(Solvable *s, Id keyname, Id *typep)
198 {
199   Repo *repo = s->repo;
200
201   if (!repo)
202     {
203       *typep = 0;
204       return 0;
205     }
206   return repo_lookup_bin_checksum(repo, s - repo->pool->solvables, keyname, typep);
207 }
208
209 const char *
210 solvable_lookup_checksum(Solvable *s, Id keyname, Id *typep)
211 {
212   const unsigned char *chk = solvable_lookup_bin_checksum(s, keyname, typep);
213   /* we need the repodata just as a reference for a pool */
214   return chk ? repodata_chk2str(s->repo->repodata, *typep, chk) : 0;
215 }
216
217 static inline const char *
218 evrid2vrstr(Pool *pool, Id evrid)
219 {
220   const char *p, *evr = id2str(pool, evrid);
221   if (!evr)
222     return evr;
223   for (p = evr; *p >= '0' && *p <= '9'; p++)
224     ;
225   return p != evr && *p == ':' ? p + 1 : evr;
226 }
227
228 char *
229 solvable_get_location(Solvable *s, unsigned int *medianrp)
230 {
231   Pool *pool;
232   int l = 0;
233   char *loc;
234   const char *mediadir, *mediafile;
235
236   *medianrp = 0;
237   if (!s->repo)
238     return 0;
239   pool = s->repo->pool;
240   *medianrp = solvable_lookup_num(s, SOLVABLE_MEDIANR, 1);
241   if (solvable_lookup_void(s, SOLVABLE_MEDIADIR))
242     mediadir = id2str(pool, s->arch);
243   else
244     mediadir = solvable_lookup_str(s, SOLVABLE_MEDIADIR);
245   if (mediadir)
246     l = strlen(mediadir) + 1;
247   if (solvable_lookup_void(s, SOLVABLE_MEDIAFILE))
248     {
249       const char *name, *evr, *arch;
250       name = id2str(pool, s->name);
251       evr = evrid2vrstr(pool, s->evr);
252       arch = id2str(pool, s->arch);
253       /* name-vr.arch.rpm */
254       loc = pool_alloctmpspace(pool, l + strlen(name) + strlen(evr) + strlen(arch) + 7);
255       if (mediadir)
256         sprintf(loc, "%s/%s-%s.%s.rpm", mediadir, name, evr, arch);
257       else
258         sprintf(loc, "%s-%s.%s.rpm", name, evr, arch);
259     }
260   else
261     {
262       mediafile = solvable_lookup_str(s, SOLVABLE_MEDIAFILE);
263       if (!mediafile)
264         return 0;
265       loc = pool_alloctmpspace(pool, l + strlen(mediafile) + 1);
266       if (mediadir)
267         sprintf(loc, "%s/%s", mediadir, mediafile);
268       else
269         strcpy(loc, mediafile);
270     }
271   return loc;
272 }
273
274 /*****************************************************************************/
275
276 static inline Id dep2name(Pool *pool, Id dep)
277 {
278   while (ISRELDEP(dep))
279     {
280       Reldep *rd = rd = GETRELDEP(pool, dep);
281       dep = rd->name;
282     }
283   return dep;
284 }
285
286 static inline int providedbyinstalled(Pool *pool, Map *installed, Id dep)
287 {
288   Id p, pp;
289   FOR_PROVIDES(p, pp, dep)
290     {
291       if (p == SYSTEMSOLVABLE)
292         return -1;
293       if (MAPTST(installed, p))
294         return 1;
295     }
296   return 0;
297 }
298
299 /*
300  * solvable_trivial_installable_map - anwers is a solvable is installable
301  * without any other installs/deinstalls.
302  * The packages considered to be installed are provided via the
303  * installedmap bitmap. A additional "conflictsmap" bitmap providing
304  * information about the conflicts of the installed packages can be
305  * used for extra speed up. Provide a NULL pointer if you do not
306  * have this information.
307  * Both maps can be created with pool_create_state_maps() or
308  * solver_create_state_maps().
309  *
310  * returns:
311  * 1:  solvable is installable without any other package changes
312  * 0:  solvable is not installable
313  * -1: solvable is installable, but doesn't constrain any installed packages
314  */
315 int
316 solvable_trivial_installable_map(Solvable *s, Map *installedmap, Map *conflictsmap)
317 {
318   Pool *pool = s->repo->pool;
319   Solvable *s2;
320   Id p, pp, *dp;
321   Id *reqp, req;
322   Id *conp, con;
323   Id *obsp, obs;
324   int r, interesting = 0;
325
326   if (conflictsmap && MAPTST(conflictsmap, s - pool->solvables))
327     return 0;
328   if (s->requires)
329     {
330       reqp = s->repo->idarraydata + s->requires;
331       while ((req = *reqp++) != 0)
332         {
333           if (req == SOLVABLE_PREREQMARKER)
334             continue;
335           r = providedbyinstalled(pool, installedmap, req);
336           if (!r)
337             return 0;
338           if (r > 0)
339             interesting = 1;
340         }
341     }
342   if (s->conflicts)
343     {
344       conp = s->repo->idarraydata + s->conflicts;
345       while ((con = *conp++) != 0)
346         {
347           if (providedbyinstalled(pool, installedmap, con))
348             return 0;
349           if (!interesting && ISRELDEP(con))
350             {
351               con = dep2name(pool, con);
352               if (providedbyinstalled(pool, installedmap, con))
353                 interesting = 1;
354             }
355         }
356     }
357   if (s->repo)
358     {
359       Repo *installed = 0;
360       if (s->obsoletes && s->repo != installed)
361         {
362           obsp = s->repo->idarraydata + s->obsoletes;
363           while ((obs = *obsp++) != 0)
364             {
365               if (providedbyinstalled(pool, installedmap, obs))
366                 return 0;
367             }
368         }
369       if (s->repo != installed)
370         {
371           FOR_PROVIDES(p, pp, s->name)
372             {
373               s2 = pool->solvables + p;
374               if (s2->repo == installed && s2->name == s->name)
375                 return 0;
376             }
377         }
378     }
379   if (!conflictsmap)
380     {
381       int i;
382
383       p = s - pool->solvables;
384       for (i = 1; i < pool->nsolvables; i++)
385         {
386           if (!MAPTST(installedmap, i))
387             continue;
388           s2 = pool->solvables + i;
389           if (!s2->conflicts)
390             continue;
391           conp = s2->repo->idarraydata + s2->conflicts;
392           while ((con = *conp++) != 0)
393             {
394               dp = pool_whatprovides_ptr(pool, con);
395               for (; *dp; dp++)
396                 if (*dp == p)
397                   return 0;
398             }
399         }
400      }
401   return interesting ? 1 : -1;
402 }
403
404 /*
405  * different interface for solvable_trivial_installable_map, where
406  * the information about the installed packages is provided
407  * by a queue.
408  */
409 int
410 solvable_trivial_installable_queue(Solvable *s, Queue *installed)
411 {
412   Pool *pool = s->repo->pool;
413   int i;
414   Id p;
415   Map installedmap;
416   int r;
417
418   map_init(&installedmap, pool->nsolvables);
419   for (i = 0; i < installed->count; i++)
420     {
421       p = installed->elements[i];
422       if (p > 0)                /* makes it work with decisionq */
423         MAPSET(&installedmap, p);
424     }
425   r = solvable_trivial_installable_map(s, &installedmap, 0);
426   map_free(&installedmap);
427   return r;
428 }
429
430 /*
431  * different interface for solvable_trivial_installable_map, where
432  * the information about the installed packages is provided
433  * by a repo containing the installed solvables.
434  */
435 int
436 solvable_trivial_installable_repo(Solvable *s, Repo *installed)
437 {
438   Pool *pool = s->repo->pool;
439   Id p;
440   Solvable *s2;
441   Map installedmap;
442   int r;
443
444   map_init(&installedmap, pool->nsolvables);
445   FOR_REPO_SOLVABLES(installed, p, s2)
446     MAPSET(&installedmap, p);
447   r = solvable_trivial_installable_map(s, &installedmap, 0);
448   map_free(&installedmap);
449   return r;
450 }
451
452
453 /*****************************************************************************/
454
455 /*
456  * Create maps containing the state of each solvable. Input is a "installed" queue,
457  * it contains all solvable ids that are considered to be installed.
458  * 
459  * The created maps can be used for solvable_trivial_installable_map(),
460  * pool_calc_duchanges(), pool_calc_installsizechange().
461  *
462  */
463 void
464 pool_create_state_maps(Pool *pool, Queue *installed, Map *installedmap, Map *conflictsmap)
465 {
466   int i;
467   Solvable *s;
468   Id p, *dp;
469   Id *conp, con;
470
471   map_init(installedmap, pool->nsolvables);
472   if (conflictsmap)
473     map_init(conflictsmap, pool->nsolvables);
474   for (i = 0; i < installed->count; i++)
475     {
476       p = installed->elements[i];
477       if (p <= 0)       /* makes it work with decisionq */
478         continue;
479       MAPSET(installedmap, p);
480       if (!conflictsmap)
481         continue;
482       s = pool->solvables + p;
483       if (!s->conflicts)
484         continue;
485       conp = s->repo->idarraydata + s->conflicts;
486       while ((con = *conp++) != 0)
487         {
488           dp = pool_whatprovides_ptr(pool, con);
489           for (; *dp; dp++)
490             MAPSET(conflictsmap, *dp);
491         }
492     }
493 }
494
495 /* Tests if two solvables have identical content. Currently
496  * both solvables need to come from the same pool */
497
498 int
499 solvable_identical(Solvable *s1, Solvable *s2)
500 {
501   unsigned int bt1, bt2;
502   Id rq1, rq2;
503   Id *reqp;
504
505   if (s1->name != s2->name)
506     return 0;
507   if (s1->arch != s2->arch)
508     return 0;
509   if (s1->evr != s2->evr)
510     return 0;
511   if (s1->vendor != s2->vendor)
512     return 0;
513
514   /* looking good, try some fancier stuff */
515   /* might also look up the package checksum here */
516   bt1 = solvable_lookup_num(s1, SOLVABLE_BUILDTIME, 0);
517   bt2 = solvable_lookup_num(s2, SOLVABLE_BUILDTIME, 0);
518   if (bt1 && bt2)
519     {
520       if (bt1 != bt2)
521         return 0;
522     }
523   else
524     {
525       /* look at requires in a last attempt to find recompiled packages */
526       rq1 = rq2 = 0;
527       if (s1->requires)
528         for (reqp = s1->repo->idarraydata + s1->requires; *reqp; reqp++)
529           rq1 ^= *reqp++;
530       if (s2->requires)
531         for (reqp = s2->repo->idarraydata + s2->requires; *reqp; reqp++)
532           rq2 ^= *reqp++;
533       if (rq1 != rq2)
534          return 0;
535     }
536   return 1;
537 }
538
539 /* return the self provide dependency of a solvable */
540 Id
541 solvable_selfprovidedep(Solvable *s)
542 {
543   Pool *pool;
544   Reldep *rd;
545   Id prov, *provp;
546
547   if (!s->repo)
548     return s->name;
549   pool = s->repo->pool;
550   if (s->provides)
551     {
552       provp = s->repo->idarraydata + s->provides;
553       while ((prov = *provp++) != 0)
554         {
555           if (!ISRELDEP(prov))
556             continue;
557           rd = GETRELDEP(pool, prov);
558           if (rd->name == s->name && rd->evr == s->evr && rd->flags == REL_EQ)
559             return prov;
560         }
561     }
562   return rel2id(pool, s->name, s->evr, REL_EQ, 1);
563 }