Imported Upstream version 0.6.23
[platform/upstream/libsolv.git] / ext / repo_autopattern.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 #define _GNU_SOURCE
9 #define _XOPEN_SOURCE
10 #include <time.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <unistd.h>
17 #include <errno.h>
18
19 #include "pool.h"
20 #include "repo.h"
21 #include "util.h"
22 #include "repo_autopattern.h"
23
24 static void
25 unescape(char *p)
26 {
27   char *q = p;
28   while (*p)
29     {
30       if (*p == '%' && p[1] && p[2])
31         {
32           int d1 = p[1], d2 = p[2];
33           if (d1 >= '0' && d1 <= '9')
34             d1 -= '0';
35           else if (d1 >= 'a' && d1 <= 'f')
36             d1 -= 'a' - 10;
37           else if (d1 >= 'A' && d1 <= 'F')
38             d1 -= 'A' - 10;
39           else
40             d1 = -1;
41           if (d2 >= '0' && d2 <= '9')
42             d2 -= '0';
43           else if (d2 >= 'a' && d2 <= 'f')
44             d2 -= 'a' - 10;
45           else if (d2 >= 'A' && d2 <= 'F')
46             d2 -= 'A' - 10;
47           else
48             d2 = -1;
49           if (d1 != -1 && d2 != -1)
50             {
51               *q++ = d1 << 4 | d2;
52               p += 3;
53               continue;
54             }
55         }
56       *q++ = *p++;
57     }
58   *q = 0;
59 }
60
61 static time_t
62 datestr2timestamp(const char *date)
63 {
64   const char *p; 
65   struct tm tm; 
66
67   if (!date || !*date)
68     return 0;
69   for (p = date; *p >= '0' && *p <= '9'; p++)
70     ;   
71   if (!*p)
72     return atoi(date);
73   memset(&tm, 0, sizeof(tm));
74   p = strptime(date, "%F%T", &tm);
75   if (!p)
76     {   
77       memset(&tm, 0, sizeof(tm));
78       p = strptime(date, "%F", &tm);
79       if (!p || *p) 
80         return 0;
81     }   
82   return timegm(&tm);
83 }
84
85 int
86 repo_add_autopattern(Repo *repo, int flags)
87 {
88   Pool *pool = repo->pool;
89   Repodata *data = 0;
90   Solvable *s, *s2;
91   Queue patq, patq2;
92   Queue prdq, prdq2;
93   Id p;
94   Id pattern_id, product_id;
95   Id autopattern_id = 0, autoproduct_id = 0;
96   int i, j;
97
98   queue_init(&patq);
99   queue_init(&patq2);
100   queue_init(&prdq);
101   queue_init(&prdq2);
102
103   if (repo == pool->installed)
104     flags |= ADD_NO_AUTOPRODUCTS;       /* no auto products for installed repos */
105
106   pattern_id = pool_str2id(pool, "pattern()", 9);
107   product_id = pool_str2id(pool, "product()", 9);
108   FOR_REPO_SOLVABLES(repo, p, s)
109     {
110       const char *n = pool_id2str(pool, s->name);
111       if (*n == 'p')
112         {
113           if (!strncmp("pattern:", n, 8))
114             {
115               queue_push(&patq, p);
116               continue;
117             }
118           else if (!strncmp("product:", n, 8))
119             {
120               queue_push(&prdq, p);
121               continue;
122             }
123         }
124       if (s->provides)
125         {
126           Id prv, *prvp = repo->idarraydata + s->provides;
127           while ((prv = *prvp++) != 0)            /* go through all provides */
128             if (ISRELDEP(prv))
129               {
130                 Reldep *rd = GETRELDEP(pool, prv);
131                 if (rd->flags != REL_EQ)
132                   continue;
133                 if (rd->name == pattern_id)
134                   {
135                     const char *evrstr = pool_id2str(pool, rd->evr);
136                     if (evrstr[0] == '.')       /* hack to allow provides that do not create a pattern */
137                       continue;
138                     if (patq2.count && patq2.elements[patq2.count - 2] == p)
139                       {
140                         /* hmm, two provides. choose by evrstr */
141                         if (strcmp(evrstr, pool_id2str(pool, patq2.elements[patq2.count - 1])) >= 0)
142                           continue;
143                         patq2.count -= 2;
144                       }
145                     queue_push2(&patq2, p, rd->evr);
146                   }
147                 if (rd->name == product_id)
148                   {
149                     const char *evrstr = pool_id2str(pool, rd->evr);
150                     if (prdq2.count && prdq2.elements[prdq2.count - 2] == p)
151                       {
152                         /* hmm, two provides. choose by evrstr */
153                         if (strcmp(evrstr, pool_id2str(pool, prdq2.elements[prdq2.count - 1])) >= 0)
154                           continue;
155                         prdq2.count -= 2;
156                       }
157                     queue_push2(&prdq2, p, rd->evr);
158                   }
159               }
160         }
161     }
162   for (i = 0; i < patq2.count; i += 2)
163     {
164       const char *pn = 0;
165       char *newname;
166       Id name, prv, *prvp;
167       const char *str;
168       unsigned long long num;
169
170       s = pool->solvables + patq2.elements[i];
171       /* construct new name */
172       newname = pool_tmpjoin(pool, "pattern:", pool_id2str(pool, patq2.elements[i + 1]), 0);
173       unescape(newname);
174       name = pool_str2id(pool, newname, 0);
175       if (name)
176         {
177           /* check if we already have that pattern */
178           for (j = 0; j < patq.count; j++)
179             {
180               s2 = pool->solvables + patq.elements[j];
181               if (s2->name == name && s2->arch == s->arch && s2->evr == s->evr)
182                 break;
183             }
184           if (j < patq.count)
185             continue;   /* yes, do not add again */
186         }
187       /* new pattern */
188       if (!name)
189         name = pool_str2id(pool, newname, 1);
190       if (!data)
191         {
192           repo_internalize(repo);       /* to make that the lookups work */
193           data = repo_add_repodata(repo, flags);
194         }
195       s2 = pool_id2solvable(pool, repo_add_solvable(repo));
196       s = pool->solvables + patq2.elements[i];  /* re-calc pointer */
197       s2->name = name;
198       s2->arch = s->arch;
199       s2->evr = s->evr;
200       s2->vendor = s->vendor;
201       /* add link requires */
202       s2->requires = repo_addid_dep(repo, s2->requires, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1) , 0);
203       /* add autopattern provides */
204       if (!autopattern_id)
205         autopattern_id = pool_str2id(pool, "autopattern()", 1);
206       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autopattern_id, s->name, REL_EQ, 1), 0);
207       /* add self provides */
208       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
209       if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
210         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_INSTALLTIME, num);
211       if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
212         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
213       if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
214         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
215       if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
216         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
217       /* fill in stuff from provides */
218       prvp = repo->idarraydata + s->provides;
219       while ((prv = *prvp++) != 0)            /* go through all provides */
220         {
221           Id evr = 0;
222           if (ISRELDEP(prv))
223             {
224               Reldep *rd = GETRELDEP(pool, prv);
225               if (rd->flags != REL_EQ)
226                 continue;
227               prv = rd->name;
228               evr = rd->evr;
229             }
230           pn = pool_id2str(pool, prv);
231           if (strncmp("pattern-", pn, 8) != 0)
232             continue;
233           newname = 0;
234           if (evr)
235             {
236               newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
237               unescape(newname);
238             }
239           if (!strncmp(pn, "pattern-category(", 17) && evr)
240             {
241               char lang[9];
242               int l = strlen(pn);
243               Id langtag;
244               if (l > 17 + 9 || pn[l - 1] != ')')
245                 continue;
246               strncpy(lang, pn + 17, l - 17 - 1);
247               lang[l - 17 - 1] = 0;
248               langtag = SOLVABLE_CATEGORY;
249               if (*lang && strcmp(lang, "en") != 0)
250                 langtag = pool_id2langid(pool, SOLVABLE_CATEGORY, lang, 1);
251               if (newname[solv_validutf8(newname)] == 0)
252                 repodata_set_str(data, s2 - pool->solvables, langtag, newname);
253               else
254                 {
255                   char *ustr = solv_latin1toutf8(newname);
256                   repodata_set_str(data, s2 - pool->solvables, langtag, ustr);
257                   solv_free(ustr);
258                 }
259             }
260           else if (!strcmp(pn, "pattern-includes()") && evr)
261             repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_INCLUDES, pool_tmpjoin(pool, "pattern:", newname, 0));
262           else if (!strcmp(pn, "pattern-extends()") && evr)
263             repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_EXTENDS, pool_tmpjoin(pool, "pattern:", newname, 0));
264           else if (!strcmp(pn, "pattern-icon()") && evr)
265             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ICON, newname);
266           else if (!strcmp(pn, "pattern-order()") && evr)
267             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ORDER, newname);
268           else if (!strcmp(pn, "pattern-visible()"))
269             {
270               if (!evr)
271                 repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
272               else
273                 repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE, newname);
274             }
275         }
276     }
277   queue_free(&patq);
278   queue_free(&patq2);
279
280   if ((flags & ADD_NO_AUTOPRODUCTS) != 0)
281     queue_empty(&prdq2);
282
283   for (i = 0; i < prdq2.count; i += 2)
284     {
285       const char *pn = 0;
286       char *newname;
287       Id name, evr = 0, prv, *prvp;
288       const char *str;
289       unsigned long long num;
290
291       s = pool->solvables + prdq2.elements[i];
292       /* construct new name */
293       newname = pool_tmpjoin(pool, "product(", pool_id2str(pool, prdq2.elements[i + 1]), ")");
294       unescape(newname);
295       name = pool_str2id(pool, newname, 0);
296       if (!name)
297         continue;       /* must have it in provides! */
298       prvp = repo->idarraydata + s->provides;
299       while ((prv = *prvp++) != 0)            /* go through all provides */
300         {
301           if (ISRELDEP(prv))
302             {
303               Reldep *rd = GETRELDEP(pool, prv);
304               if (rd->name == name && rd->flags == REL_EQ)
305                 {
306                   evr = rd->evr;
307                   break;
308                 }
309             }
310         }
311       if (!prv)
312         continue;       /* not found in provides */
313       newname = pool_tmpjoin(pool, "product:", pool_id2str(pool, prdq2.elements[i + 1]), 0);
314       unescape(newname);
315       name = pool_str2id(pool, newname, 0);
316       if (name)
317         {
318           /* check if we already have that product */
319           for (j = 0; j < prdq.count; j++)
320             {
321               s2 = pool->solvables + prdq.elements[j];
322               if (s2->name == name && s2->arch == s->arch && s2->evr == evr)
323                 break;
324             }
325           if (j < prdq.count)
326             continue;   /* yes, do not add again */
327         }
328       /* new product */
329       if (!name)
330         name = pool_str2id(pool, newname, 1);
331       if (!data)
332         {
333           repo_internalize(repo);       /* to make that the lookups work */
334           data = repo_add_repodata(repo, flags);
335         }
336       if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
337         continue;               /* eek, not for installed packages, please! */
338       s2 = pool_id2solvable(pool, repo_add_solvable(repo));
339       s = pool->solvables + prdq2.elements[i];  /* re-calc pointer */
340       s2->name = name;
341       s2->arch = s->arch;
342       s2->evr = evr;
343       s2->vendor = s->vendor;
344       /* add link requires */
345       s2->requires = repo_addid_dep(repo, s2->requires, prv, 0);
346       if (!autoproduct_id)
347         autoproduct_id = pool_str2id(pool, "autoproduct()", 1);
348       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autoproduct_id, s->name, REL_EQ, 1), 0);
349       /* add self provides */
350       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
351       if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
352         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
353       if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
354         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
355       if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
356         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
357       if ((str = solvable_lookup_str(s, SOLVABLE_DISTRIBUTION)) != 0)
358         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DISTRIBUTION, str);
359       /* fill in stuff from provides */
360       prvp = repo->idarraydata + s->provides;
361       while ((prv = *prvp++) != 0)            /* go through all provides */
362         {
363           Id evr = 0;
364           if (ISRELDEP(prv))
365             {
366               Reldep *rd = GETRELDEP(pool, prv);
367               if (rd->flags != REL_EQ)
368                 continue;
369               prv = rd->name;
370               evr = rd->evr;
371             }
372           pn = pool_id2str(pool, prv);
373           if (strncmp("product-", pn, 8) != 0)
374             continue;
375           newname = 0;
376           if (evr)
377             {
378               newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
379               unescape(newname);
380             }
381           if (!strcmp(pn, "product-label()") && evr)
382             repodata_set_str(data, s2 - pool->solvables, PRODUCT_SHORTLABEL, newname);
383           else if (!strcmp(pn, "product-register-target()") && evr)
384             repodata_set_str(data, s2 - pool->solvables, PRODUCT_REGISTER_TARGET, newname);
385           else if (!strcmp(pn, "product-register-flavor()") && evr)
386             repodata_set_str(data, s2 - pool->solvables, PRODUCT_REGISTER_FLAVOR, newname);
387           else if (!strcmp(pn, "product-type()") && evr)
388             repodata_set_str(data, s2 - pool->solvables, PRODUCT_TYPE, newname);
389           else if (!strcmp(pn, "product-cpeid()") && evr)
390             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_CPEID, newname);
391           else if (!strcmp(pn, "product-flags()") && evr)
392             repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_FLAGS, newname);
393           else if (!strcmp(pn, "product-updates-repoid()") && evr)
394             {
395               Id h = repodata_new_handle(data);
396               repodata_set_str(data, h, PRODUCT_UPDATES_REPOID, newname);
397               repodata_add_flexarray(data, s2 - pool->solvables, PRODUCT_UPDATES, h);
398             }
399           else if (!strcmp(pn, "product-endoflife()"))
400             {
401               /* FATE#320699: Support tri-state product-endoflife (tag absent, present but nodate(0), present + date) */
402               repodata_set_num(data, s2 - pool->solvables, PRODUCT_ENDOFLIFE,(evr ? datestr2timestamp(newname) : 0) );
403             }
404           else if (!strncmp(pn, "product-url(", 12) && evr && pn[12] && pn[13] && strlen(pn + 12) < 32)
405             {
406               char type[34];
407               strcpy(type, pn + 12);
408               type[strlen(type) - 1] = 0;       /* closing ) */
409               repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL_TYPE, type);
410               repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL, newname);
411             }
412         }
413     }
414   queue_free(&prdq);
415   queue_free(&prdq2);
416
417   if (data && !(flags & REPO_NO_INTERNALIZE))
418     repodata_internalize(data);
419   else if (!data && !(flags & REPO_NO_INTERNALIZE))
420     repo_internalize(repo);
421   return 0;
422 }
423