Imported Upstream version 0.6.15
[platform/upstream/libsolv.git] / src / linkedpkg.c
1 /*
2  * Copyright (c) 2013, SUSE Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * linkedpkg.c
10  *
11  * Linked packages are "pseudo" packages that are bound to real packages but
12  * contain different information (name/summary/description). They are normally
13  * somehow generated from the real packages, either when the repositories are
14  * created or automatically from the packages by looking at the provides.
15  *
16  * We currently support:
17  *
18  * application:
19  *   created from AppStream appdata xml in the repository (which is generated
20  *   from files in /usr/share/appdata)
21  *
22  * product:
23  *   created from product data in the repository (which is generated from files
24  *   in /etc/products.d). In the future we may switch to using product()
25  *   provides of packages.
26  *
27  * pattern:
28  *   created from pattern() provides of packages.
29  *
30  */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <assert.h>
37
38 #include "pool.h"
39 #include "repo.h"
40 #include "evr.h"
41 #include "linkedpkg.h"
42
43 #ifdef ENABLE_LINKED_PKGS
44
45 void
46 find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
47 {
48   Id req = 0;
49   Id prv = 0;
50   Id p, pp;
51   Id pkgname = 0, appdataid = 0;
52
53   /* find appdata requires */
54   if (s->requires)
55     {
56       Id *reqp = s->repo->idarraydata + s->requires;
57       while ((req = *reqp++) != 0)            /* go through all requires */
58         {
59           if (ISRELDEP(req))
60             continue;
61           if (!strncmp("appdata(", pool_id2str(pool, req), 8))
62             appdataid = req;
63           else
64             pkgname = req;
65         }
66     }
67   req = appdataid ? appdataid : pkgname;
68   if (!req)
69     return;
70   /* find application-appdata provides */
71   if (s->provides)
72     {
73       Id *prvp = s->repo->idarraydata + s->provides;
74       const char *reqs = pool_id2str(pool, req);
75       const char *prvs;
76       while ((prv = *prvp++) != 0)            /* go through all provides */
77         {
78           if (ISRELDEP(prv))
79             continue;
80           prvs = pool_id2str(pool, prv);
81           if (strncmp("application-appdata(", prvs, 20))
82             continue;
83           if (appdataid)
84             {
85               if (!strcmp(prvs + 12, reqs))
86                 break;
87             }
88           else
89             {
90               int reqsl = strlen(reqs);
91               if (!strncmp(prvs + 20, reqs, reqsl) && !strcmp(prvs + 20 + reqsl, ")"))
92                 break;
93             }
94         }
95     }
96   if (!prv)
97     return;     /* huh, no provides found? */
98   /* now link em */
99   FOR_PROVIDES(p, pp, req)
100     if (pool->solvables[p].repo == s->repo)
101       if (!pkgname || pool->solvables[p].name == pkgname)
102         queue_push(qr, p);
103   if (!qr->count && pkgname && appdataid)
104     {
105       /* huh, no matching package? try without pkgname filter */
106       FOR_PROVIDES(p, pp, req)
107         if (pool->solvables[p].repo == s->repo)
108           queue_push(qr, p);
109     }
110   if (qp)
111     {
112       FOR_PROVIDES(p, pp, prv)
113         if (pool->solvables[p].repo == s->repo)
114           queue_push(qp, p);
115     }
116   if (reqidp)
117     *reqidp = req;
118   if (prvidp)
119     *prvidp = prv;
120 }
121
122 void
123 find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
124 {
125   Id p, pp, namerelid;
126   char *str;
127   unsigned int sbt = 0;
128
129   /* search for project requires */
130   namerelid = 0;
131   if (s->requires)
132     {
133       Id req, *reqp = s->repo->idarraydata + s->requires;
134       const char *nn = pool_id2str(pool, s->name);
135       int nnl = strlen(nn);
136       while ((req = *reqp++) != 0)            /* go through all requires */
137         if (ISRELDEP(req))
138           {
139             const char *rn;
140             Reldep *rd = GETRELDEP(pool, req);
141             if (rd->flags != REL_EQ || rd->evr != s->evr)
142               continue;
143             rn = pool_id2str(pool, rd->name);
144             if (!strncmp(rn, "product(", 8) && !strncmp(rn + 8, nn + 8, nnl - 8) && !strcmp( rn + nnl, ")"))
145               {
146                 namerelid = req;
147                 break;
148               }
149           }
150     }
151   if (!namerelid)
152     {
153       /* too bad. construct from scratch */
154       str = pool_tmpjoin(pool, pool_id2str(pool, s->name), ")", 0);
155       str[7] = '(';
156       namerelid = pool_rel2id(pool, pool_str2id(pool, str, 1), s->evr, REL_EQ, 1);
157     }
158   FOR_PROVIDES(p, pp, namerelid)
159     {
160       Solvable *ps = pool->solvables + p;
161       if (ps->repo != s->repo || ps->arch != s->arch)
162         continue;
163       queue_push(qr, p);
164     }
165   if (qr->count > 1)
166     {
167       /* multiple providers. try buildtime filter */
168       sbt = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
169       if (sbt)
170         {
171           unsigned int bt;
172           int i, j;
173           int filterqp = 1;
174           for (i = j = 0; i < qr->count; i++)
175             {
176               bt = solvable_lookup_num(pool->solvables + qr->elements[i], SOLVABLE_BUILDTIME, 0);
177               if (!bt)
178                 filterqp = 0;   /* can't filter */
179               if (!bt || bt == sbt)
180                 qr->elements[j++] = qr->elements[i];
181             }
182           if (j)
183             qr->count = j;
184           if (!j || !filterqp)
185             sbt = 0;    /* filter failed */
186         }
187     }
188   if (!qr->count && s->repo == pool->installed)
189     {
190       /* oh no! Look up reference file */
191       Dataiterator di;
192       const char *refbasename = solvable_lookup_str(s, PRODUCT_REFERENCEFILE);
193       dataiterator_init(&di, pool, s->repo, 0, SOLVABLE_FILELIST, refbasename, SEARCH_STRING);
194       while (dataiterator_step(&di))
195         queue_push(qr, di.solvid);
196       dataiterator_free(&di);
197       if (qp)
198         {
199           dataiterator_init(&di, pool, s->repo, 0, PRODUCT_REFERENCEFILE, refbasename, SEARCH_STRING);
200           while (dataiterator_step(&di))
201             queue_push(qp, di.solvid);
202           dataiterator_free(&di);
203         }
204     }
205   else if (qp)
206     {
207       /* find qp */
208       FOR_PROVIDES(p, pp, s->name)
209         {
210           Solvable *ps = pool->solvables + p;
211           if (s->name != ps->name || ps->repo != s->repo || ps->arch != s->arch || s->evr != ps->evr)
212             continue;
213           if (sbt && solvable_lookup_num(ps, SOLVABLE_BUILDTIME, 0) != sbt)
214             continue;
215           queue_push(qp, p);
216         }
217     }
218   if (reqidp)
219     *reqidp = namerelid;
220   if (prvidp)
221     *prvidp = solvable_selfprovidedep(s);
222 }
223
224 void
225 find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
226 {
227   Id p, pp, *pr, apevr = 0, aprel = 0;
228
229   /* check if autopattern */
230   if (!s->provides)
231     return;
232   for (pr = s->repo->idarraydata + s->provides; (p = *pr++) != 0; )
233     if (ISRELDEP(p))
234       {
235         Reldep *rd = GETRELDEP(pool, p);
236         if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
237           {
238             aprel = p;
239             apevr = rd->evr;
240             break;
241           }
242       }
243   if (!apevr)
244     return;
245   FOR_PROVIDES(p, pp, apevr)
246     {
247       Solvable *s2 = pool->solvables + p;
248       if (s2->repo == s->repo && s2->name == apevr && s2->evr == s->evr && s2->vendor == s->vendor)
249         queue_push(qr, p);
250     }
251   if (qp)
252     {
253       FOR_PROVIDES(p, pp, aprel)
254         {
255           Solvable *s2 = pool->solvables + p;
256           if (s2->repo == s->repo && s2->evr == s->evr && s2->vendor == s->vendor)
257             queue_push(qp, p);
258         }
259     }
260   if (reqidp)
261     *reqidp = apevr;
262   if (prvidp)
263     *prvidp = aprel;
264 }
265
266 /* the following two functions are used in solvable_lookup_str_base to do
267  * translated lookups on the product/pattern packages
268  */
269 Id
270 find_autopattern_name(Pool *pool, Solvable *s)
271 {
272   Id prv, *prvp;
273   if (!s->provides)
274     return 0;
275   for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
276     if (ISRELDEP(prv))
277       {
278         Reldep *rd = GETRELDEP(pool, prv);
279         if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
280           return strncmp(pool_id2str(pool, rd->evr), "pattern:", 8) != 0 ? rd->evr : 0;
281       }
282   return 0;
283 }
284
285 Id
286 find_autoproduct_name(Pool *pool, Solvable *s)
287 {
288   Id prv, *prvp;
289   if (!s->provides)
290     return 0;
291   for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
292     if (ISRELDEP(prv))
293       {
294         Reldep *rd = GETRELDEP(pool, prv);
295         if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autoproduct()"))
296           return strncmp(pool_id2str(pool, rd->evr), "product:", 8) != 0 ? rd->evr : 0;
297       }
298   return 0;
299 }
300
301 void
302 find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
303 {
304   const char *name = pool_id2str(pool, s->name);
305   if (name[0] == 'a' && !strncmp("application:", name, 12))
306     find_application_link(pool, s, reqidp, qr, prvidp, qp);
307   else if (name[0] == 'p' && !strncmp("pattern:", name, 7))
308     find_pattern_link(pool, s, reqidp, qr, prvidp, qp);
309   else if (name[0] == 'p' && !strncmp("product:", name, 8))
310     find_product_link(pool, s, reqidp, qr, prvidp, qp);
311 }
312
313 static int
314 name_min_max(Pool *pool, Solvable *s, Id *namep, Id *minp, Id *maxp)
315 {
316   Queue q;
317   Id qbuf[4];
318   Id name, min, max;
319   int i;
320
321   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
322   find_package_link(pool, s, 0, &q, 0, 0);
323   if (!q.count)
324     {
325       queue_free(&q);
326       return 0;
327     }
328   s = pool->solvables + q.elements[0];
329   name = s->name;
330   min = max = s->evr;
331   for (i = 1; i < q.count; i++)
332     {
333       s = pool->solvables + q.elements[i];
334       if (s->name != name)
335         {
336           queue_free(&q);
337           return 0;
338         }
339       if (s->evr == min || s->evr == max)
340         continue;
341       if (pool_evrcmp(pool, min, s->evr, EVRCMP_COMPARE) >= 0)
342         min = s->evr;
343       else if (min == max || pool_evrcmp(pool, max, s->evr, EVRCMP_COMPARE) <= 0)
344         max = s->evr;
345     }
346   queue_free(&q);
347   *namep = name;
348   *minp = min;
349   *maxp = max;
350   return 1;
351 }
352
353 int
354 pool_link_evrcmp(Pool *pool, Solvable *s1, Solvable *s2)
355 {
356   Id name1, evrmin1, evrmax1;
357   Id name2, evrmin2, evrmax2;
358
359   if (s1->name != s2->name)
360     return 0;   /* can't compare */
361   if (!name_min_max(pool, s1, &name1, &evrmin1, &evrmax1))
362     return 0;
363   if (!name_min_max(pool, s2, &name2, &evrmin2, &evrmax2))
364     return 0;
365   /* compare linked names */
366   if (name1 != name2)
367     return 0;
368   if (evrmin1 == evrmin2 && evrmax1 == evrmax2)
369     return 0;
370   /* now compare evr intervals */
371   if (evrmin1 == evrmax1 && evrmin2 == evrmax2)
372     return pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE);
373   if (evrmin1 != evrmax2 && pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE) > 0)
374     return 1;
375   if (evrmax1 != evrmin2 && pool_evrcmp(pool, evrmax1, evrmin2, EVRCMP_COMPARE) < 0)
376     return -1;
377   return 0;
378 }
379
380
381 #endif