fix a bug in the application link code
[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 "linkedpkg.h"
41
42 #ifdef ENABLE_LINKED_PKGS
43
44 void
45 find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
46 {
47   Id req = 0;
48   Id prv = 0;
49   Id p, pp;
50
51   /* find appdata requires */
52   if (s->requires)
53     {
54       Id *reqp = s->repo->idarraydata + s->requires;
55       while ((req = *reqp++) != 0)            /* go through all requires */
56         {
57           if (ISRELDEP(req))
58             continue;
59           if (!strncmp("appdata(", pool_id2str(pool, req), 8))
60             break;
61         }
62     }
63   if (!req)
64     return;
65   /* find application-appdata provides */
66   if (s->provides)
67     {
68       Id *prvp = s->repo->idarraydata + s->provides;
69       while ((prv = *prvp++) != 0)            /* go through all provides */
70         {
71           if (ISRELDEP(prv))
72             continue;
73           if (strncmp("application-appdata(", pool_id2str(pool, prv), 20))
74             continue;
75           if (!strcmp(pool_id2str(pool, prv) + 12, pool_id2str(pool, req)))
76             break;
77         }
78     }
79   if (!prv)
80     return;
81   /* now link em */
82   FOR_PROVIDES(p, pp, req)
83     if (pool->solvables[p].repo == s->repo)
84       queue_push(qr, p);
85   if (qp)
86     {
87       FOR_PROVIDES(p, pp, prv)
88         if (pool->solvables[p].repo == s->repo)
89           queue_push(qp, pp);
90     }
91   if (reqidp)
92     *reqidp = req;
93   if (prvidp)
94     *prvidp = prv;
95 }
96
97 void
98 find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
99 {
100   Id n = s - pool->solvables;
101   Id p, pp, namerelid;
102   char *str;
103
104   if (pool->nscallback)
105     {
106       Id buddy = pool->nscallback(pool, pool->nscallbackdata, NAMESPACE_PRODUCTBUDDY, n);
107       if (buddy > 0 && buddy != SYSTEMSOLVABLE && buddy != n && buddy < pool->nsolvables)
108         {
109           if (reqidp)
110             *reqidp = solvable_selfprovidedep(pool->solvables + buddy);
111           queue_push(qr, buddy);
112           if (prvidp)
113             *prvidp = solvable_selfprovidedep(s);
114           if (qp)
115             queue_push(qp, n);
116           return;
117         }
118     }
119   /* search for project requires */
120   namerelid = 0;
121   if (s->requires)
122     {
123       Id req, *reqp = s->repo->idarraydata + s->requires;
124       const char *nn = pool_id2str(pool, s->name);
125       int nnl = strlen(nn);
126       while ((req = *reqp++) != 0)            /* go through all requires */
127         if (ISRELDEP(req))
128           {
129             const char *rn;
130             Reldep *rd = GETRELDEP(pool, req);
131             if (rd->flags != REL_EQ || rd->evr != s->evr)
132               continue;
133             rn = pool_id2str(pool, rd->name);
134             if (!strncmp(rn, "product(", 8) && !strncmp(rn + 8, nn + 8, nnl - 8) && !strcmp( rn + nnl, ")"))
135               {
136                 namerelid = req;
137                 break;
138               }
139           }
140     }
141   if (!namerelid)
142     {
143       /* too bad. construct from scratch */
144       str = pool_tmpjoin(pool, pool_id2str(pool, s->name), ")", 0);
145       str[7] = '(';
146       namerelid = pool_rel2id(pool, pool_str2id(pool, str, 1), s->evr, REL_EQ, 1);
147     }
148   FOR_PROVIDES(p, pp, namerelid)
149     {
150       Solvable *ps = pool->solvables + p;
151       if (ps->repo != s->repo || ps->arch != s->arch)
152         continue;
153       queue_push(qr, p);
154     }
155   if (!qr->count && s->repo == pool->installed)
156     {
157       /* oh no! Look up reference file */
158       Dataiterator di;
159       const char *refbasename = solvable_lookup_str(s, PRODUCT_REFERENCEFILE);
160       dataiterator_init(&di, pool, s->repo, 0, SOLVABLE_FILELIST, refbasename, SEARCH_STRING);
161       while (dataiterator_step(&di))
162         queue_push(qr, di.solvid);
163       dataiterator_free(&di);
164       if (qp)
165         {
166           dataiterator_init(&di, pool, s->repo, 0, PRODUCT_REFERENCEFILE, refbasename, SEARCH_STRING);
167           while (dataiterator_step(&di))
168             queue_push(qp, di.solvid);
169           dataiterator_free(&di);
170         }
171     }
172   else if (qp)
173     {
174       /* find qp */
175       FOR_PROVIDES(p, pp, s->name)
176         {
177           Solvable *ps = pool->solvables + p;
178           if (s->name != ps->name || ps->repo != s->repo || ps->arch != s->arch || s->evr != ps->evr)
179             continue;
180           queue_push(qp, p);
181         }
182     }
183   if (reqidp)
184     *reqidp = namerelid;
185   if (prvidp)
186     *prvidp = solvable_selfprovidedep(s);
187 }
188
189 void
190 find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
191 {
192   Id p, pp, *pr, apevr = 0, aprel = 0;
193
194   /* check if autopattern */
195   if (!s->provides)
196     return;
197   for (pr = s->repo->idarraydata + s->provides; (p = *pr++) != 0; )
198     if (ISRELDEP(p))
199       {
200         Reldep *rd = GETRELDEP(pool, p);
201         if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
202           {
203             aprel = p;
204             apevr = rd->evr;
205             break;
206           }
207       }
208   if (!apevr)
209     return;
210   FOR_PROVIDES(p, pp, apevr)
211     {
212       Solvable *s2 = pool->solvables + p;
213       if (s2->repo == s->repo && s2->name == apevr && s2->evr == s->evr && s2->vendor == s->vendor)
214         queue_push(qr, p);
215     }
216   if (qp)
217     {
218       FOR_PROVIDES(p, pp, aprel)
219         {
220           Solvable *s2 = pool->solvables + p;
221           if (s2->repo == s->repo && s2->evr == s->evr && s2->vendor == s->vendor)
222             queue_push(qp, p);
223         }
224     }
225   if (reqidp)
226     *reqidp = apevr;
227   if (prvidp)
228     *prvidp = aprel;
229 }
230
231 Id
232 find_autopattern_name(Pool *pool, Solvable *s)
233 {
234   Id prv, *prvp;
235   if (!s->provides)
236     return 0;
237   for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
238     if (ISRELDEP(prv))
239       {
240         Reldep *rd = GETRELDEP(pool, prv);
241         if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
242           return strncmp(pool_id2str(pool, rd->evr), "pattern:", 8) != 0 ? rd->evr : 0;
243       }
244   return 0;
245 }
246
247 void
248 find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
249 {
250   const char *name = pool_id2str(pool, s->name);
251   if (name[0] == 'a' && !strncmp("application:", name, 12))
252     find_application_link(pool, s, reqidp, qr, prvidp, qp);
253   else if (name[0] == 'p' && !strncmp("pattern:", name, 7))
254     find_pattern_link(pool, s, reqidp, qr, prvidp, qp);
255   else if (name[0] == 'p' && !strncmp("product:", name, 8))
256     find_product_link(pool, s, reqidp, qr, prvidp, qp);
257 }
258
259 #endif