5132394f579a306448e9af1ccf760a439cb50748
[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()") && !evr)
269             repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
270         }
271     }
272   queue_free(&patq);
273   queue_free(&patq2);
274
275   if ((flags & ADD_NO_AUTOPRODUCTS) != 0)
276     queue_empty(&prdq2);
277
278   for (i = 0; i < prdq2.count; i += 2)
279     {
280       const char *pn = 0;
281       char *newname;
282       Id name, evr = 0, prv, *prvp;
283       const char *str;
284       unsigned long long num;
285
286       s = pool->solvables + prdq2.elements[i];
287       /* construct new name */
288       newname = pool_tmpjoin(pool, "product(", pool_id2str(pool, prdq2.elements[i + 1]), ")");
289       unescape(newname);
290       name = pool_str2id(pool, newname, 0);
291       if (!name)
292         continue;       /* must have it in provides! */
293       prvp = repo->idarraydata + s->provides;
294       while ((prv = *prvp++) != 0)            /* go through all provides */
295         {
296           if (ISRELDEP(prv))
297             {
298               Reldep *rd = GETRELDEP(pool, prv);
299               if (rd->name == name && rd->flags == REL_EQ)
300                 {
301                   evr = rd->evr;
302                   break;
303                 }
304             }
305         }
306       if (!prv)
307         continue;       /* not found in provides */
308       newname = pool_tmpjoin(pool, "product:", pool_id2str(pool, prdq2.elements[i + 1]), 0);
309       unescape(newname);
310       name = pool_str2id(pool, newname, 0);
311       if (name)
312         {
313           /* check if we already have that product */
314           for (j = 0; j < prdq.count; j++)
315             {
316               s2 = pool->solvables + prdq.elements[j];
317               if (s2->name == name && s2->arch == s->arch && s2->evr == evr)
318                 break;
319             }
320           if (j < prdq.count)
321             continue;   /* yes, do not add again */
322         }
323       /* new product */
324       if (!name)
325         name = pool_str2id(pool, newname, 1);
326       if (!data)
327         {
328           repo_internalize(repo);       /* to make that the lookups work */
329           data = repo_add_repodata(repo, flags);
330         }
331       if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
332         continue;               /* eek, not for installed packages, please! */
333       s2 = pool_id2solvable(pool, repo_add_solvable(repo));
334       s = pool->solvables + prdq2.elements[i];  /* re-calc pointer */
335       s2->name = name;
336       s2->arch = s->arch;
337       s2->evr = evr;
338       s2->vendor = s->vendor;
339       /* add link requires */
340       s2->requires = repo_addid_dep(repo, s2->requires, prv, 0);
341       if (!autoproduct_id)
342         autoproduct_id = pool_str2id(pool, "autoproduct()", 1);
343       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autoproduct_id, s->name, REL_EQ, 1), 0);
344       /* add self provides */
345       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
346       if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
347         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
348       if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
349         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
350       if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
351         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
352       if ((str = solvable_lookup_str(s, SOLVABLE_DISTRIBUTION)) != 0)
353         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DISTRIBUTION, str);
354       /* fill in stuff from provides */
355       prvp = repo->idarraydata + s->provides;
356       while ((prv = *prvp++) != 0)            /* go through all provides */
357         {
358           Id evr = 0;
359           if (ISRELDEP(prv))
360             {
361               Reldep *rd = GETRELDEP(pool, prv);
362               if (rd->flags != REL_EQ)
363                 continue;
364               prv = rd->name;
365               evr = rd->evr;
366             }
367           pn = pool_id2str(pool, prv);
368           if (strncmp("product-", pn, 8) != 0)
369             continue;
370           newname = 0;
371           if (evr)
372             {
373               newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
374               unescape(newname);
375             }
376           if (!strcmp(pn, "product-label()") && evr)
377             repodata_set_str(data, s2 - pool->solvables, PRODUCT_SHORTLABEL, newname);
378           else if (!strcmp(pn, "product-register-target()") && evr)
379             repodata_set_str(data, s2 - pool->solvables, PRODUCT_REGISTER_TARGET, newname);
380           else if (!strcmp(pn, "product-type()") && evr)
381             repodata_set_str(data, s2 - pool->solvables, PRODUCT_TYPE, newname);
382           else if (!strcmp(pn, "product-cpeid()") && evr)
383             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_CPEID, newname);
384           else if (!strcmp(pn, "product-flags()") && evr)
385             repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_FLAGS, newname);
386           else if (!strcmp(pn, "product-updates-repoid()") && evr)
387             {
388               Id h = repodata_new_handle(data);
389               repodata_set_str(data, h, PRODUCT_UPDATES_REPOID, newname);
390               repodata_add_flexarray(data, s2 - pool->solvables, PRODUCT_UPDATES, h);
391             }
392           else if (!strcmp(pn, "product-endoflife()") && evr)
393             {
394               time_t t = datestr2timestamp(newname);
395               if (t)
396                 repodata_set_num(data, s2 - pool->solvables, PRODUCT_ENDOFLIFE, t);
397             }
398           else if (!strncmp(pn, "product-url(", 12) && evr && pn[12] && pn[13] && strlen(pn + 12) < 32)
399             {
400               char type[34];
401               strcpy(type, pn + 12);
402               type[strlen(type) - 1] = 0;       /* closing ) */
403               repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL_TYPE, type);
404               repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL, newname);
405             }
406         }
407     }
408   queue_free(&prdq);
409   queue_free(&prdq2);
410
411   if (data && !(flags & REPO_NO_INTERNALIZE))
412     repodata_internalize(data);
413   else if (!data && !(flags & REPO_NO_INTERNALIZE))
414     repo_internalize(repo);
415   return 0;
416 }
417