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