- do not use obsolete interface in libsolv library itself
[platform/upstream/libsolv.git] / ext / repo_content.c
1 /*
2  * repo_content.c
3  *
4  * Parses 'content' file into .solv
5  * A 'content' file is the repomd.xml of the susetags format
6  *
7  * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata/content for a description
8  * of the syntax
9  *
10  *
11  * Copyright (c) 2007, Novell Inc.
12  *
13  * This program is licensed under the BSD license, read LICENSE.BSD
14  * for further information
15  */
16
17 #include <sys/types.h>
18 #include <limits.h>
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24
25 #include "pool.h"
26 #include "repo.h"
27 #include "util.h"
28 #include "chksum.h"
29 #include "repo_content.h"
30 #define DISABLE_SPLIT
31 #include "tools_util.h"
32
33 /* split off a word, return null terminated pointer to it.
34  * return NULL if there is no word left. */
35 static char *
36 splitword(char **lp)
37 {
38   char *w, *l = *lp;
39
40   while (*l == ' ' || *l == '\t')
41     l++;
42   w = *l ? l : 0;
43   while (*l && *l != ' ' && *l != '\t')
44     l++;
45   if (*l)
46     *l++ = 0;           /* terminate word */
47   while (*l == ' ' || *l == '\t')
48     l++;                /* convenience: advance to next word */
49   *lp = l;
50   return w;
51 }
52
53 struct parsedata {
54   Repo *repo;
55   char *tmp;
56   int tmpl;
57
58   const char *tmpvers;
59   const char *tmprel;
60 };
61
62 /*
63  * dependency relations
64  */
65
66 static char *flagtab[] = {
67   ">",
68   "=",
69   ">=",
70   "<",
71   "!=",
72   "<="
73 };
74
75
76 /*
77  * join up to three strings into one
78  */
79
80 static char *
81 join(struct parsedata *pd, const char *s1, const char *s2, const char *s3)
82 {
83   int l = 1;
84   char *p;
85
86   if (s1)
87     l += strlen(s1);
88   if (s2)
89     l += strlen(s2);
90   if (s3)
91     l += strlen(s3);
92   if (l > pd->tmpl)
93     {
94       pd->tmpl = l + 256;
95       pd->tmp = sat_realloc(pd->tmp, pd->tmpl);
96     }
97   p = pd->tmp;
98   if (s1)
99     {
100       strcpy(p, s1);
101       p += strlen(s1);
102     }
103   if (s2)
104     {
105       strcpy(p, s2);
106       p += strlen(s2);
107     }
108   if (s3)
109     {
110       strcpy(p, s3);
111       p += strlen(s3);
112     }
113   *p = 0;
114   return pd->tmp;
115 }
116
117
118 /*
119  * add dependency to pool
120  * OBSOLETES product:SUSE_LINUX product:openSUSE < 11.0 package:openSUSE < 11.0
121  */
122
123 static unsigned int
124 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker)
125 {
126   char *name;
127   Id id;
128
129   while ((name = splitword(&line)) != 0)
130     {
131       /* Hack, as the content file adds 'package:' for package
132          dependencies sometimes.  */
133       if (!strncmp (name, "package:", 8))
134         name += 8;
135       id = pool_str2id(pool, name, 1);
136       if (*line == '<' || *line == '>' || *line == '=') /* rel follows */
137         {
138           char *rel = splitword(&line);
139           char *evr = splitword(&line);
140           int flags;
141
142           if (!rel || !evr)
143             {
144               pool_debug(pool, SAT_FATAL, "repo_content: bad relation '%s %s'\n", name, rel);
145               continue;
146             }
147           for (flags = 0; flags < 6; flags++)
148             if (!strcmp(rel, flagtab[flags]))
149               break;
150           if (flags == 6)
151             {
152               pool_debug(pool, SAT_FATAL, "repo_content: unknown relation '%s'\n", rel);
153               continue;
154             }
155           id = pool_rel2id(pool, id, pool_str2id(pool, evr, 1), flags + 1, 1);
156         }
157       olddeps = repo_addid_dep(pd->repo, olddeps, id, marker);
158     }
159   return olddeps;
160 }
161
162
163 /*
164  * split value and add to pool
165  */
166
167 static void
168 add_multiple_strings(Repodata *data, Id handle, Id name, char *value)
169 {
170   char *str;
171
172   while ((str = splitword(&value)) != 0)
173     repodata_add_poolstr_array(data, handle, name, str);
174 }
175
176 /*
177  * split value and add to pool
178  */
179
180 static void
181 add_multiple_urls(Repodata *data, Id handle, char *value, Id type)
182 {
183   char *url;
184
185   while ((url = splitword(&value)) != 0)
186     {
187       repodata_add_poolstr_array(data, handle, PRODUCT_URL, url);
188       repodata_add_idarray(data, handle, PRODUCT_URL_TYPE, type);
189     }
190 }
191
192
193
194 /*
195  * add 'content' to repo
196  *
197  */
198
199 void
200 repo_add_content(Repo *repo, FILE *fp, int flags)
201 {
202   Pool *pool = repo->pool;
203   char *line, *linep;
204   int aline;
205   Solvable *s;
206   struct parsedata pd;
207   Repodata *data;
208   Id handle = 0;
209   int contentstyle = 0;
210   char *descrdir = 0;
211   char *datadir = 0;
212   char *defvendor = 0;
213
214   int i = 0;
215
216   /* architectures
217      we use the first architecture in BASEARCHS or noarch
218      for the product. At the end we create (clone) the product
219      for each one of the remaining architectures
220      we allow max 4 archs
221   */
222   unsigned int numotherarchs = 0;
223   Id *otherarchs = 0;
224
225   memset(&pd, 0, sizeof(pd));
226   line = sat_malloc(1024);
227   aline = 1024;
228
229   pd.repo = repo;
230   linep = line;
231   s = 0;
232
233   data = repo_add_repodata(repo, flags);
234
235   for (;;)
236     {
237       char *key, *value;
238
239       /* read line into big-enough buffer */
240       if (linep - line + 16 > aline)
241         {
242           aline = linep - line;
243           line = sat_realloc(line, aline + 512);
244           linep = line + aline;
245           aline += 512;
246         }
247       if (!fgets(linep, aline - (linep - line), fp))
248         break;
249       linep += strlen(linep);
250       if (linep == line || linep[-1] != '\n')
251         continue;
252       while ( --linep > line && ( linep[-1] == ' ' ||  linep[-1] == '\t' ) )
253         ; /* skip trailing ws */
254       *linep = 0;
255       linep = line;
256
257       /* expect "key value" lines */
258       value = line;
259       key = splitword(&value);
260
261       if (key)
262         {
263 #if 0
264           fprintf (stderr, "key %s, value %s\n", key, value);
265 #endif
266
267 #define istag(x) (!strcmp (key, x))
268 #define code10 (contentstyle == 10)
269 #define code11 (contentstyle == 11)
270
271
272           if (istag ("CONTENTSTYLE"))
273             {
274               if (contentstyle)
275                 pool_debug(pool, SAT_ERROR, "repo_content: 'CONTENTSTYLE' must be first line of 'content'\n");
276               contentstyle = atoi(value);
277               continue;
278             }
279           if (!contentstyle)
280             contentstyle = 10;
281
282           /* repository tags */
283           /* we also replicate some of them into the product solvables
284            * to be backward compatible */
285
286           if (istag ("REPOID"))
287             {
288               repodata_add_poolstr_array(data, SOLVID_META, REPOSITORY_REPOID, value);
289               continue;
290             }
291
292           if (istag ("DESCRDIR"))
293             {
294               if (descrdir)
295                 free(descrdir);
296               else
297                 repodata_set_str(data, SOLVID_META, SUSETAGS_DESCRDIR, value);
298               if (s)
299                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, value);
300               descrdir = strdup(value);
301               continue;
302             }
303           if (istag ("DATADIR"))
304             {
305               if (datadir)
306                 free(datadir);
307               else
308                 repodata_set_str(data, SOLVID_META, SUSETAGS_DATADIR, value);
309               if (s)
310                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, value);
311               datadir = strdup(value);
312               continue;
313             }
314           if (istag ("VENDOR"))
315             {
316               if (defvendor)
317                 free(defvendor);
318               else
319                 repodata_set_poolstr(data, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
320               if (s)
321                 s->vendor = pool_str2id(pool, value, 1);
322               defvendor = strdup(value);
323               continue;
324             }
325
326           if (istag ("META") || istag ("HASH") || istag ("KEY"))
327             {
328               char *checksumtype, *checksum;
329               Id fh, type;
330               int l;
331
332               if ((checksumtype = splitword(&value)) == 0)
333                 continue;
334               if ((checksum = splitword(&value)) == 0)
335                 continue;
336               if (!*value)
337                 continue;
338               type = sat_chksum_str2type(checksumtype);
339               if (!type)
340                 {
341                   fprintf(stderr, "Unknown checksum type: %s: %s\n", value, checksumtype);
342                   continue;
343                 }
344               l = sat_chksum_len(type);
345               if (strlen(checksum) != 2 * l)
346                 {
347                   fprintf(stderr, "Invalid checksum length: %s: for %s\n", value, checksum);
348                   continue;
349                 }
350               fh = repodata_new_handle(data);
351               repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
352               repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
353               repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
354               repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
355               continue;
356             }
357
358           /* product tags */
359
360           if ((code10 && istag ("PRODUCT"))
361               || (code11 && istag ("NAME")))
362             {
363               if (s && !s->name)
364                 {
365                   /* this solvable was created without seeing a
366                      PRODUCT entry, just set the name and continue */
367                   s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
368                   continue;
369                 }
370               if (s)
371                 {
372                   /* finish old solvable */
373                   if (!s->arch)
374                     s->arch = ARCH_NOARCH;
375                   if (!s->evr)
376                     s->evr = ID_EMPTY;
377                   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
378                     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
379                   if (code10)
380                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
381                 }
382               /* create new solvable */
383               s = pool_id2solvable(pool, repo_add_solvable(repo));
384               repodata_extend(data, s - pool->solvables);
385               handle = s - pool->solvables;
386               s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
387               if (datadir)
388                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, datadir);
389               if (descrdir)
390                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
391               if (defvendor)
392                 s->vendor = pool_str2id(pool, defvendor, 1);
393               continue;
394             }
395
396           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
397              from here on.  */
398           if (!s)
399             {
400               s = pool_id2solvable(pool, repo_add_solvable(repo));
401               repodata_extend(data, s - pool->solvables);
402               handle = s - pool->solvables;
403             }
404
405           if (istag ("VERSION"))
406             pd.tmpvers = strdup(value);
407           else if (istag ("RELEASE"))
408             pd.tmprel = strdup(value);
409           else if (code11 && istag ("DISTRIBUTION"))
410             repodata_set_str(data, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
411           else if (istag ("UPDATEURLS"))
412             add_multiple_urls(data, handle, value, pool_str2id(pool, "update", 1));
413           else if (istag ("EXTRAURLS"))
414             add_multiple_urls(data, handle, value, pool_str2id(pool, "extra", 1));
415           else if (istag ("OPTIONALURLS"))
416             add_multiple_urls(data, handle, value, pool_str2id(pool, "optional", 1));
417           else if (istag ("RELNOTESURL"))
418             add_multiple_urls(data, handle, value, pool_str2id(pool, "releasenotes", 1));
419           else if (istag ("SHORTLABEL"))
420             repodata_set_str(data, s - pool->solvables, PRODUCT_SHORTLABEL, value);
421           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
422             repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, value);
423           else if (!strncmp (key, "LABEL.", 6))
424             repodata_set_str(data, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
425           else if (istag ("FLAGS"))
426             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
427           else if (istag ("VENDOR"))    /* actually already handled above */
428             s->vendor = pool_str2id(pool, value, 1);
429           else if (istag ("BASEARCHS"))
430             {
431               char *arch;
432
433               if ((arch = splitword(&value)) != 0)
434                 {
435                   s->arch = pool_str2id(pool, arch, 1);
436                   while ((arch = splitword(&value)) != 0)
437                     {
438                        otherarchs = sat_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
439                        otherarchs[numotherarchs++] = pool_str2id(pool, arch, 1);
440                     }
441                 }
442             }
443
444           /*
445            * Every tag below is Code10 only
446            *
447            */
448
449           if (code10 && istag ("DISTPRODUCT"))
450             /* DISTPRODUCT is for registration and Yast, not for the solver. */
451             repodata_set_str(data, s - pool->solvables, PRODUCT_DISTPRODUCT, value);
452           else if (code10 && istag ("DISTVERSION"))
453             /* DISTVERSION is for registration and Yast, not for the solver. */
454             repodata_set_str(data, s - pool->solvables, PRODUCT_DISTVERSION, value);
455           else if (code10 && istag ("ARCH"))
456             /* Theoretically we want to have the best arch of the given
457                modifiers which still is compatible with the system
458                arch.  We don't know the latter here, though.  */
459             s->arch = ARCH_NOARCH;
460           else if (code10 && istag ("PREREQUIRES"))
461             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
462           else if (code10 && istag ("REQUIRES"))
463             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
464           else if (code10 && istag ("PROVIDES"))
465             s->provides = adddep(pool, &pd, s->provides, value, 0);
466           else if (code10 && istag ("CONFLICTS"))
467             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
468           else if (code10 && istag ("OBSOLETES"))
469             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
470           else if (code10 && istag ("RECOMMENDS"))
471             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
472           else if (code10 && istag ("SUGGESTS"))
473             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
474           else if (code10 && istag ("SUPPLEMENTS"))
475             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
476           else if (code10 && istag ("ENHANCES"))
477             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
478           /* FRESHENS doesn't seem to exist.  */
479           else if (code10 && istag ("TYPE"))
480             repodata_set_str(data, s - pool->solvables, PRODUCT_TYPE, value);
481
482           /* XXX do something about LINGUAS and ARCH?
483           * <ma>: Don't think so. zypp does not use or propagate them.
484           */
485 #undef istag
486         }
487       else
488         pool_debug(pool, SAT_ERROR, "repo_content: malformed line: %s\n", line);
489     }
490
491   if (datadir)
492     free(datadir);
493   if (descrdir)
494     free(descrdir);
495   if (defvendor)
496     free(defvendor);
497
498   if (s && !s->name)
499     {
500       pool_debug(pool, SAT_FATAL, "repo_content: 'content' incomplete, no product solvable created!\n");
501       repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
502       s = 0;
503     }
504   if (s)
505     {
506       if (pd.tmprel)
507         s->evr = makeevr(pool, join(&pd, pd.tmpvers, "-", pd.tmprel));
508       else
509         s->evr = makeevr(pool, pd.tmpvers);
510       pd.tmpvers = sat_free((void *)pd.tmpvers);
511       pd.tmprel = sat_free((void *)pd.tmprel);
512
513       if (!s->arch)
514         s->arch = ARCH_NOARCH;
515       if (!s->evr)
516         s->evr = ID_EMPTY;
517       if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
518         s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
519       if (code10)
520         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
521
522       /* now for every other arch, clone the product except the architecture */
523       for (i = 0; i < numotherarchs; ++i)
524         {
525           Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
526           repodata_extend(data, p - pool->solvables);
527           p->name = s->name;
528           p->evr = s->evr;
529           p->vendor = s->vendor;
530           p->arch = otherarchs[i];
531
532           /* self provides */
533           if (s->name && p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
534               p->provides = repo_addid_dep(repo, p->provides, pool_rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
535
536           /* now merge the attributes */
537           repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
538         }
539     }
540
541   if (pd.tmp)
542     sat_free(pd.tmp);
543   sat_free(line);
544   sat_free(otherarchs);
545   join_freemem();
546   if (!(flags & REPO_NO_INTERNALIZE))
547     repodata_internalize(data);
548 }