Imported Upstream version 0.7.1
[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 /* we just look at the repodata keys and do not iterate
86  * over the solvables, because iterating would mean a
87  * load of stub repodata areas */
88 static void
89 find_langkeys(Repo *repo, Id keyname, Queue *q)
90 {
91   Pool *pool = repo->pool;
92   int rid;
93   int i;
94   const char *keyname_str;
95   size_t keyname_len;
96
97   keyname_str = pool_id2str(pool, keyname);
98   keyname_len = strlen(keyname_str);
99   queue_empty(q);
100   for (rid = 1; rid < repo->nrepodata; rid++)
101     {
102       Repodata *data = repo_id2repodata(repo, rid);
103       for (i = 1; i < data->nkeys; i++)
104         {
105           const char *s = pool_id2str(pool, data->keys[i].name);
106           if (!strncmp(s, keyname_str, keyname_len) && s[keyname_len] == ':')
107             queue_pushunique(q, data->keys[i].name);
108         }
109     }
110 }
111
112 int
113 repo_add_autopattern(Repo *repo, int flags)
114 {
115   Pool *pool = repo->pool;
116   Repodata *data = 0;
117   Solvable *s, *s2;
118   Queue patq, patq2;
119   Queue prdq, prdq2;
120   Id p;
121   Id pattern_id, product_id;
122   Id autopattern_id = 0, autoproduct_id = 0;
123   int i, j;
124   Queue categorykeys;
125
126   queue_init(&patq);
127   queue_init(&patq2);
128   queue_init(&prdq);
129   queue_init(&prdq2);
130
131   if (repo == pool->installed)
132     flags |= ADD_NO_AUTOPRODUCTS;       /* no auto products for installed repos */
133
134   pattern_id = pool_str2id(pool, "pattern()", 9);
135   product_id = pool_str2id(pool, "product()", 9);
136
137   queue_init(&categorykeys);
138   FOR_REPO_SOLVABLES(repo, p, s)
139     {
140       const char *n = pool_id2str(pool, s->name);
141       if (*n == 'p')
142         {
143           if (!strncmp("pattern:", n, 8))
144             {
145               queue_push(&patq, p);
146               continue;
147             }
148           else if (!strncmp("product:", n, 8))
149             {
150               queue_push(&prdq, p);
151               continue;
152             }
153         }
154       if (s->provides)
155         {
156           Id prv, *prvp = repo->idarraydata + s->provides;
157           while ((prv = *prvp++) != 0)            /* go through all provides */
158             if (ISRELDEP(prv))
159               {
160                 Reldep *rd = GETRELDEP(pool, prv);
161                 if (rd->flags != REL_EQ)
162                   continue;
163                 if (rd->name == pattern_id)
164                   {
165                     const char *evrstr = pool_id2str(pool, rd->evr);
166                     if (evrstr[0] == '.')       /* hack to allow provides that do not create a pattern */
167                       continue;
168                     if (patq2.count && patq2.elements[patq2.count - 2] == p)
169                       {
170                         /* hmm, two provides. choose by evrstr */
171                         if (strcmp(evrstr, pool_id2str(pool, patq2.elements[patq2.count - 1])) >= 0)
172                           continue;
173                         patq2.count -= 2;
174                       }
175                     queue_push2(&patq2, p, rd->evr);
176                   }
177                 if (rd->name == product_id)
178                   {
179                     const char *evrstr = pool_id2str(pool, rd->evr);
180                     if (prdq2.count && prdq2.elements[prdq2.count - 2] == p)
181                       {
182                         /* hmm, two provides. choose by evrstr */
183                         if (strcmp(evrstr, pool_id2str(pool, prdq2.elements[prdq2.count - 1])) >= 0)
184                           continue;
185                         prdq2.count -= 2;
186                       }
187                     queue_push2(&prdq2, p, rd->evr);
188                   }
189               }
190         }
191     }
192   if (patq2.count)
193     {
194       find_langkeys(repo, SOLVABLE_CATEGORY, &categorykeys);
195       queue_unshift(&categorykeys, SOLVABLE_CATEGORY);
196     }
197   for (i = 0; i < patq2.count; i += 2)
198     {
199       const char *pn = 0;
200       char *newname;
201       Id name, prv, *prvp;
202       const char *str;
203       unsigned long long num;
204
205       s = pool->solvables + patq2.elements[i];
206       /* construct new name */
207       newname = pool_tmpjoin(pool, "pattern:", pool_id2str(pool, patq2.elements[i + 1]), 0);
208       unescape(newname);
209       name = pool_str2id(pool, newname, 0);
210       if (name)
211         {
212           /* check if we already have that pattern */
213           for (j = 0; j < patq.count; j++)
214             {
215               s2 = pool->solvables + patq.elements[j];
216               if (s2->name == name && s2->arch == s->arch && s2->evr == s->evr)
217                 break;
218             }
219           if (j < patq.count)
220             continue;   /* yes, do not add again */
221         }
222       /* new pattern */
223       if (!name)
224         name = pool_str2id(pool, newname, 1);
225       if (!data)
226         {
227           repo_internalize(repo);       /* to make that the lookups work */
228           data = repo_add_repodata(repo, flags);
229         }
230       s2 = pool_id2solvable(pool, repo_add_solvable(repo));
231       s = pool->solvables + patq2.elements[i];  /* re-calc pointer */
232       s2->name = name;
233       s2->arch = s->arch;
234       s2->evr = s->evr;
235       s2->vendor = s->vendor;
236       /* add link requires */
237       s2->requires = repo_addid_dep(repo, s2->requires, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1) , 0);
238       /* add autopattern provides */
239       if (!autopattern_id)
240         autopattern_id = pool_str2id(pool, "autopattern()", 1);
241       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autopattern_id, s->name, REL_EQ, 1), 0);
242       /* add self provides */
243       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
244       if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
245         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_INSTALLTIME, num);
246       if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
247         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
248       if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
249         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
250       if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
251         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
252       /* fill in stuff from provides */
253       prvp = repo->idarraydata + s->provides;
254       while ((prv = *prvp++) != 0)            /* go through all provides */
255         {
256           Id evr = 0;
257           if (ISRELDEP(prv))
258             {
259               Reldep *rd = GETRELDEP(pool, prv);
260               if (rd->flags != REL_EQ)
261                 continue;
262               prv = rd->name;
263               evr = rd->evr;
264             }
265           pn = pool_id2str(pool, prv);
266           if (strncmp("pattern-", pn, 8) != 0)
267             continue;
268           newname = 0;
269           if (evr)
270             {
271               newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
272               unescape(newname);
273             }
274           if (!strncmp(pn, "pattern-category(", 17) && evr)
275             {
276               char lang[9];
277               int l = strlen(pn);
278               Id langtag;
279               if (l > 17 + 9 || pn[l - 1] != ')')
280                 continue;
281               strncpy(lang, pn + 17, l - 17 - 1);
282               lang[l - 17 - 1] = 0;
283               langtag = SOLVABLE_CATEGORY;
284               if (*lang && strcmp(lang, "en") != 0)
285                 langtag = pool_id2langid(pool, SOLVABLE_CATEGORY, lang, 1);
286               if (newname[solv_validutf8(newname)] == 0)
287                 repodata_set_str(data, s2 - pool->solvables, langtag, newname);
288               else
289                 {
290                   char *ustr = solv_latin1toutf8(newname);
291                   repodata_set_str(data, s2 - pool->solvables, langtag, ustr);
292                   solv_free(ustr);
293                 }
294             }
295           else if (!strcmp(pn, "pattern-includes()") && evr)
296             repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_INCLUDES, pool_tmpjoin(pool, "pattern:", newname, 0));
297           else if (!strcmp(pn, "pattern-extends()") && evr)
298             repodata_add_poolstr_array(data, s2 - pool->solvables, SOLVABLE_EXTENDS, pool_tmpjoin(pool, "pattern:", newname, 0));
299           else if (!strcmp(pn, "pattern-icon()") && evr)
300             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ICON, newname);
301           else if (!strcmp(pn, "pattern-order()") && evr)
302             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ORDER, newname);
303           else if (!strcmp(pn, "pattern-visible()"))
304             {
305               if (!evr)
306                 repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
307               else
308                 repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE, newname);
309             }
310         }
311       /* also try to copy the pattern category from the solvable */
312       for (j = 0; j < categorykeys.count; j++)
313         {
314           Id catkey = categorykeys.elements[j];
315           if ((str = solvable_lookup_str(s, catkey)) != 0)
316             repodata_set_str(data, s2 - pool->solvables, catkey, str);
317         }
318     }
319   queue_free(&patq);
320   queue_free(&patq2);
321   queue_free(&categorykeys);
322
323   if ((flags & ADD_NO_AUTOPRODUCTS) != 0)
324     queue_empty(&prdq2);
325
326   for (i = 0; i < prdq2.count; i += 2)
327     {
328       const char *pn = 0;
329       char *newname;
330       Id name, evr = 0, prv, *prvp;
331       const char *str;
332       unsigned long long num;
333
334       s = pool->solvables + prdq2.elements[i];
335       /* construct new name */
336       newname = pool_tmpjoin(pool, "product(", pool_id2str(pool, prdq2.elements[i + 1]), ")");
337       unescape(newname);
338       name = pool_str2id(pool, newname, 0);
339       if (!name)
340         continue;       /* must have it in provides! */
341       prvp = repo->idarraydata + s->provides;
342       while ((prv = *prvp++) != 0)            /* go through all provides */
343         {
344           if (ISRELDEP(prv))
345             {
346               Reldep *rd = GETRELDEP(pool, prv);
347               if (rd->name == name && rd->flags == REL_EQ)
348                 {
349                   evr = rd->evr;
350                   break;
351                 }
352             }
353         }
354       if (!prv)
355         continue;       /* not found in provides */
356       newname = pool_tmpjoin(pool, "product:", pool_id2str(pool, prdq2.elements[i + 1]), 0);
357       unescape(newname);
358       name = pool_str2id(pool, newname, 0);
359       if (name)
360         {
361           /* check if we already have that product */
362           for (j = 0; j < prdq.count; j++)
363             {
364               s2 = pool->solvables + prdq.elements[j];
365               if (s2->name == name && s2->arch == s->arch && s2->evr == evr)
366                 break;
367             }
368           if (j < prdq.count)
369             continue;   /* yes, do not add again */
370         }
371       /* new product */
372       if (!name)
373         name = pool_str2id(pool, newname, 1);
374       if (!data)
375         {
376           repo_internalize(repo);       /* to make that the lookups work */
377           data = repo_add_repodata(repo, flags);
378         }
379       if ((num = solvable_lookup_num(s, SOLVABLE_INSTALLTIME, 0)) != 0)
380         continue;               /* eek, not for installed packages, please! */
381       s2 = pool_id2solvable(pool, repo_add_solvable(repo));
382       s = pool->solvables + prdq2.elements[i];  /* re-calc pointer */
383       s2->name = name;
384       s2->arch = s->arch;
385       s2->evr = evr;
386       s2->vendor = s->vendor;
387       /* add link requires */
388       s2->requires = repo_addid_dep(repo, s2->requires, prv, 0);
389       if (!autoproduct_id)
390         autoproduct_id = pool_str2id(pool, "autoproduct()", 1);
391       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, autoproduct_id, s->name, REL_EQ, 1), 0);
392       /* add self provides */
393       s2->provides = repo_addid_dep(repo, s2->provides, pool_rel2id(pool, s2->name, s2->evr, REL_EQ, 1), 0);
394       if ((num = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0)) != 0)
395         repodata_set_num(data, s2 - pool->solvables, SOLVABLE_BUILDTIME, num);
396       if ((str = solvable_lookup_str(s, SOLVABLE_SUMMARY)) != 0)
397         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_SUMMARY, str);
398       if ((str = solvable_lookup_str(s, SOLVABLE_DESCRIPTION)) != 0)
399         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DESCRIPTION, str);
400       if ((str = solvable_lookup_str(s, SOLVABLE_DISTRIBUTION)) != 0)
401         repodata_set_str(data, s2 - pool->solvables, SOLVABLE_DISTRIBUTION, str);
402       /* fill in stuff from provides */
403       prvp = repo->idarraydata + s->provides;
404       while ((prv = *prvp++) != 0)            /* go through all provides */
405         {
406           Id evr = 0;
407           if (ISRELDEP(prv))
408             {
409               Reldep *rd = GETRELDEP(pool, prv);
410               if (rd->flags != REL_EQ)
411                 continue;
412               prv = rd->name;
413               evr = rd->evr;
414             }
415           pn = pool_id2str(pool, prv);
416           if (strncmp("product-", pn, 8) != 0)
417             continue;
418           newname = 0;
419           if (evr)
420             {
421               newname = pool_tmpjoin(pool, pool_id2str(pool, evr), 0, 0);
422               unescape(newname);
423             }
424           if (!strcmp(pn, "product-label()") && evr)
425             repodata_set_str(data, s2 - pool->solvables, PRODUCT_SHORTLABEL, newname);
426           else if (!strcmp(pn, "product-register-target()") && evr)
427             repodata_set_str(data, s2 - pool->solvables, PRODUCT_REGISTER_TARGET, newname);
428           else if (!strcmp(pn, "product-register-flavor()") && evr)
429             repodata_set_str(data, s2 - pool->solvables, PRODUCT_REGISTER_FLAVOR, newname);
430           else if (!strcmp(pn, "product-type()") && evr)
431             repodata_set_str(data, s2 - pool->solvables, PRODUCT_TYPE, newname);
432           else if (!strcmp(pn, "product-cpeid()") && evr)
433             repodata_set_str(data, s2 - pool->solvables, SOLVABLE_CPEID, newname);
434           else if (!strcmp(pn, "product-flags()") && evr)
435             repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_FLAGS, newname);
436           else if (!strcmp(pn, "product-updates-repoid()") && evr)
437             {
438               Id h = repodata_new_handle(data);
439               repodata_set_str(data, h, PRODUCT_UPDATES_REPOID, newname);
440               repodata_add_flexarray(data, s2 - pool->solvables, PRODUCT_UPDATES, h);
441             }
442           else if (!strcmp(pn, "product-endoflife()"))
443             {
444               /* FATE#320699: Support tri-state product-endoflife (tag absent, present but nodate(0), present + date) */
445               repodata_set_num(data, s2 - pool->solvables, PRODUCT_ENDOFLIFE,(evr ? datestr2timestamp(newname) : 0) );
446             }
447           else if (!strncmp(pn, "product-url(", 12) && evr && pn[12] && pn[13] && strlen(pn + 12) < 32)
448             {
449               char type[34];
450               strcpy(type, pn + 12);
451               type[strlen(type) - 1] = 0;       /* closing ) */
452               repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL_TYPE, type);
453               repodata_add_poolstr_array(data, s2 - pool->solvables, PRODUCT_URL, newname);
454             }
455         }
456     }
457   queue_free(&prdq);
458   queue_free(&prdq2);
459
460   if (data && !(flags & REPO_NO_INTERNALIZE))
461     repodata_internalize(data);
462   else if (!data && !(flags & REPO_NO_INTERNALIZE))
463     repo_internalize(repo);
464   return 0;
465 }
466