ignore pattern() provides that start with '.'
[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] == '.')
137                       continue;
138                     queue_push2(&patq2, p, rd->evr);
139                     break;
140                   }
141                 if (rd->name == product_id)
142                   {
143                     queue_push2(&prdq2, p, rd->evr);
144                     break;
145                   }
146               }
147         }
148     }
149   for (i = 0; i < patq2.count; i += 2)
150     {
151       const char *pn = 0;
152       char *newname;
153       Id name, prv, *prvp;
154       const char *str;
155       unsigned long long num;
156
157       s = pool->solvables + patq2.elements[i];
158       /* construct new name */
159       newname = pool_tmpjoin(pool, "pattern:", pool_id2str(pool, patq2.elements[i + 1]), 0);
160       unescape(newname);
161       name = pool_str2id(pool, newname, 0);
162       if (name)
163         {
164           /* check if we already have that pattern */
165           for (j = 0; j < patq.count; j++)
166             {
167               s2 = pool->solvables + patq.elements[j];
168               if (s2->name == name && s2->arch == s->arch && s2->evr == s->evr)
169                 break;
170             }
171           if (j < patq.count)
172             continue;   /* yes, do not add again */
173         }
174       /* new pattern */
175       if (!name)
176         name = pool_str2id(pool, newname, 1);
177       if (!data)
178         {
179           repo_internalize(repo);       /* to make that the lookups work */
180           data = repo_add_repodata(repo, flags);
181         }
182       s2 = pool_id2solvable(pool, repo_add_solvable(repo));
183       s = pool->solvables + patq2.elements[i];  /* re-calc pointer */
184       s2->name = name;
185       s2->arch = s->arch;
186       s2->evr = s->evr;
187       s2->vendor = s->vendor;
188       /* add link requires */
189       s2->requires = repo_addid_dep(repo, s2->requires, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1) , 0);
190       /* add autopattern provides */
191       if (!autopattern_id)
192         autopattern_id = pool_str2id(pool, "autopattern()", 1);
193       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autopattern_id, s->name, REL_EQ, 1), 0);
194       /* add self provides */
195       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
196       if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
197         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_INSTALLTIME, num);
198       if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
199         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
200       if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
201         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
202       if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
203         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
204       /* fill in stuff from provides */
205       prvp = repo->idarraydata + s->provides;
206       while ((prv = *prvp++) != 0)            /* go through all provides */
207         {
208           Id evr = 0;
209           if (ISRELDEP(prv))
210             {
211               Reldep *rd = GETRELDEP(pool, prv);
212               if (rd->flags != REL_EQ)
213                 continue;
214               prv = rd->name;
215               evr = rd->evr;
216             }
217           pn = pool_id2str(pool, prv);
218           if (strncmp("pattern-", pn, 8) != 0)
219             continue;
220           newname = 0;
221           if (evr)
222             {
223               newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
224               unescape(newname);
225             }
226           if (!strncmp(pn, "pattern-category(", 17) && evr)
227             {
228               char lang[9];
229               int l = strlen(pn);
230               Id langtag;
231               if (l > 17 + 9 || pn[l - 1] != ')')
232                 continue;
233               strncpy(lang, pn + 17, l - 17 - 1);
234               lang[l - 17 - 1] = 0;
235               langtag = SOLVABLE_CATEGORY;
236               if (*lang && strcmp(lang, "en") != 0)
237                 langtag = pool_id2langid(pool, SOLVABLE_CATEGORY, lang, 1);
238               if (newname[solv_validutf8(newname)] == 0)
239                 repodata_set_str(data, s2 - pool->solvables, langtag, newname);
240               else
241                 {
242                   char *ustr = solv_latin1toutf8(newname);
243                   repodata_set_str(data, s2 - pool->solvables, langtag, ustr);
244                   solv_free(ustr);
245                 }
246             }
247           else if (!strcmp(pn, "pattern-includes()") && evr)
248             repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_INCLUDES, pool_tmpjoin(pool, "pattern:", newname, 0));
249           else if (!strcmp(pn, "pattern-extends()") && evr)
250             repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_EXTENDS, pool_tmpjoin(pool, "pattern:", newname, 0));
251           else if (!strcmp(pn, "pattern-icon()") && evr)
252             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ICON, newname);
253           else if (!strcmp(pn, "pattern-order()") && evr)
254             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ORDER, newname);
255           else if (!strcmp(pn, "pattern-visible()") && !evr)
256             repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
257         }
258     }
259   queue_free(&patq);
260   queue_free(&patq2);
261
262   if ((flags & ADD_NO_AUTOPRODUCTS) != 0)
263     queue_empty(&prdq2);
264
265   for (i = 0; i < prdq2.count; i += 2)
266     {
267       const char *pn = 0;
268       char *newname;
269       Id name, evr = 0, prv, *prvp;
270       const char *str;
271       unsigned long long num;
272
273       s = pool->solvables + prdq2.elements[i];
274       /* construct new name */
275       newname = pool_tmpjoin(pool, "product(", pool_id2str(pool, prdq2.elements[i + 1]), ")");
276       unescape(newname);
277       name = pool_str2id(pool, newname, 0);
278       if (!name)
279         continue;       /* must have it in provides! */
280       prvp = repo->idarraydata + s->provides;
281       while ((prv = *prvp++) != 0)            /* go through all provides */
282         {
283           if (ISRELDEP(prv))
284             {
285               Reldep *rd = GETRELDEP(pool, prv);
286               if (rd->name == name && rd->flags == REL_EQ)
287                 {
288                   evr = rd->evr;
289                   break;
290                 }
291             }
292         }
293       if (!prv)
294         continue;       /* not found in provides */
295       newname = pool_tmpjoin(pool, "product:", pool_id2str(pool, prdq2.elements[i + 1]), 0);
296       unescape(newname);
297       name = pool_str2id(pool, newname, 0);
298       if (name)
299         {
300           /* check if we already have that product */
301           for (j = 0; j < prdq.count; j++)
302             {
303               s2 = pool->solvables + prdq.elements[j];
304               if (s2->name == name && s2->arch == s->arch && s2->evr == evr)
305                 break;
306             }
307           if (j < prdq.count)
308             continue;   /* yes, do not add again */
309         }
310       /* new product */
311       if (!name)
312         name = pool_str2id(pool, newname, 1);
313       if (!data)
314         {
315           repo_internalize(repo);       /* to make that the lookups work */
316           data = repo_add_repodata(repo, flags);
317         }
318       if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
319         continue;               /* eek, not for installed packages, please! */
320       s2 = pool_id2solvable(pool, repo_add_solvable(repo));
321       s = pool->solvables + prdq2.elements[i];  /* re-calc pointer */
322       s2->name = name;
323       s2->arch = s->arch;
324       s2->evr = evr;
325       s2->vendor = s->vendor;
326       /* add link requires */
327       s2->requires = repo_addid_dep(repo, s2->requires, prv, 0);
328       if (!autoproduct_id)
329         autoproduct_id = pool_str2id(pool, "autoproduct()", 1);
330       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autoproduct_id, s->name, REL_EQ, 1), 0);
331       /* add self provides */
332       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
333       if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
334         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
335       if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
336         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
337       if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
338         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
339       if ((str = solvable_lookup_str(s, SOLVABLE_DISTRIBUTION)) != 0)
340         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DISTRIBUTION, str);
341       /* fill in stuff from provides */
342       prvp = repo->idarraydata + s->provides;
343       while ((prv = *prvp++) != 0)            /* go through all provides */
344         {
345           Id evr = 0;
346           if (ISRELDEP(prv))
347             {
348               Reldep *rd = GETRELDEP(pool, prv);
349               if (rd->flags != REL_EQ)
350                 continue;
351               prv = rd->name;
352               evr = rd->evr;
353             }
354           pn = pool_id2str(pool, prv);
355           if (strncmp("product-", pn, 8) != 0)
356             continue;
357           newname = 0;
358           if (evr)
359             {
360               newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
361               unescape(newname);
362             }
363           if (!strcmp(pn, "product-label()") && evr)
364             repodata_set_str(data, s2 - pool->solvables, PRODUCT_SHORTLABEL, newname);
365           else if (!strcmp(pn, "product-type()") && evr)
366             repodata_set_str(data, s2 - pool->solvables, PRODUCT_TYPE, newname);
367           else if (!strcmp(pn, "product-cpeid()") && evr)
368             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_CPEID, newname);
369           else if (!strcmp(pn, "product-flags()") && evr)
370             repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_FLAGS, newname);
371           else if (!strcmp(pn, "product-updates-repoid()") && evr)
372             {
373               Id h = repodata_new_handle(data);
374               repodata_set_str(data, h, PRODUCT_UPDATES_REPOID, newname);
375               repodata_add_flexarray(data, s2 - pool->solvables, PRODUCT_UPDATES, h);
376             }
377           else if (!strcmp(pn, "product-endoflife()") && evr)
378             {
379               time_t t = datestr2timestamp(newname);
380               if (t)
381                 repodata_set_num(data, s2 - pool->solvables, PRODUCT_ENDOFLIFE, t);
382             }
383           else if (!strncmp(pn, "product-url(", 12) && evr && pn[12] && pn[13] && strlen(pn + 12) < 32)
384             {
385               char type[34];
386               strcpy(type, pn + 12);
387               type[strlen(type) - 1] = 0;       /* closing ) */
388               repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL_TYPE, type);
389               repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL, newname);
390             }
391         }
392     }
393   queue_free(&prdq);
394   queue_free(&prdq2);
395
396   if (data && !(flags & REPO_NO_INTERNALIZE))
397     repodata_internalize(data);
398   else if (!data && !(flags & REPO_NO_INTERNALIZE))
399     repo_internalize(repo);
400   return 0;
401 }
402