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