support DISTRO in content parser
[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   int res = 0;
217
218   /* architectures
219      we use the first architecture in BASEARCHS or noarch
220      for the product. At the end we create (clone) the product
221      for each one of the remaining architectures
222      we allow max 4 archs
223   */
224   unsigned int numotherarchs = 0;
225   Id *otherarchs = 0;
226
227   memset(&pd, 0, sizeof(pd));
228   line = solv_malloc(1024);
229   aline = 1024;
230
231   pd.repo = repo;
232   linep = line;
233   s = 0;
234
235   data = repo_add_repodata(repo, flags);
236
237   for (;;)
238     {
239       char *key, *value;
240
241       /* read line into big-enough buffer */
242       if (linep - line + 16 > aline)
243         {
244           aline = linep - line;
245           line = solv_realloc(line, aline + 512);
246           linep = line + aline;
247           aline += 512;
248         }
249       if (!fgets(linep, aline - (linep - line), fp))
250         break;
251       linep += strlen(linep);
252       if (linep == line || linep[-1] != '\n')
253         continue;
254       while ( --linep > line && ( linep[-1] == ' ' ||  linep[-1] == '\t' ) )
255         ; /* skip trailing ws */
256       *linep = 0;
257       linep = line;
258
259       /* expect "key value" lines */
260       value = line;
261       key = splitword(&value);
262
263       if (key)
264         {
265 #if 0
266           fprintf (stderr, "key %s, value %s\n", key, value);
267 #endif
268
269 #define istag(x) (!strcmp (key, x))
270 #define code10 (contentstyle == 10)
271 #define code11 (contentstyle == 11)
272
273
274           if (istag ("CONTENTSTYLE"))
275             {
276               if (contentstyle)
277                 pool_debug(pool, SOLV_ERROR, "repo_content: 'CONTENTSTYLE' must be first line of 'content'\n");
278               contentstyle = atoi(value);
279               continue;
280             }
281           if (!contentstyle)
282             contentstyle = 10;
283
284           /* repository tags */
285           /* we also replicate some of them into the product solvables
286            * to be backward compatible */
287
288           if (istag ("REPOID"))
289             {
290               repodata_add_poolstr_array(data, SOLVID_META, REPOSITORY_REPOID, value);
291               continue;
292             }
293           if (istag ("DISTRO"))
294             {
295               Id dh = repodata_new_handle(data);
296               char *p;
297               /* like with createrepo --distro */
298               if ((p = strchr(value, ',')) != 0)
299                 {
300                   *p++ = 0;
301                   if (*value)
302                     repodata_set_poolstr(data, dh, REPOSITORY_PRODUCT_CPEID, value);
303                 }
304               else
305                 p = value;
306               if (*p)
307                 repodata_set_str(data, dh, REPOSITORY_PRODUCT_LABEL, p);
308               repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DISTROS, dh);
309               continue;
310             }
311
312           if (istag ("DESCRDIR"))
313             {
314               if (descrdir)
315                 free(descrdir);
316               else
317                 repodata_set_str(data, SOLVID_META, SUSETAGS_DESCRDIR, value);
318               if (s)
319                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, value);
320               descrdir = solv_strdup(value);
321               continue;
322             }
323           if (istag ("DATADIR"))
324             {
325               if (datadir)
326                 free(datadir);
327               else
328                 repodata_set_str(data, SOLVID_META, SUSETAGS_DATADIR, value);
329               if (s)
330                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, value);
331               datadir = solv_strdup(value);
332               continue;
333             }
334           if (istag ("VENDOR"))
335             {
336               if (defvendor)
337                 free(defvendor);
338               else
339                 repodata_set_poolstr(data, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
340               if (s)
341                 s->vendor = pool_str2id(pool, value, 1);
342               defvendor = solv_strdup(value);
343               continue;
344             }
345
346           if (istag ("META") || istag ("HASH") || istag ("KEY"))
347             {
348               char *checksumtype, *checksum;
349               Id fh, type;
350               int l;
351
352               if ((checksumtype = splitword(&value)) == 0)
353                 continue;
354               if ((checksum = splitword(&value)) == 0)
355                 continue;
356               if (!*value)
357                 continue;
358               type = solv_chksum_str2type(checksumtype);
359               if (!type)
360                 {
361                   pool_error(pool, -1, "%s: unknown checksum type '%s'", value, checksumtype);
362                   res = 1;
363                   continue;
364                 }
365               l = solv_chksum_len(type);
366               if (strlen(checksum) != 2 * l)
367                 {
368                   pool_error(pool, -1, "%s: invalid checksum length for %s", value, checksumtype);
369                   res = 1;
370                   continue;
371                 }
372               fh = repodata_new_handle(data);
373               repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
374               repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
375               repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
376               repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
377               continue;
378             }
379
380           /* product tags */
381
382           if ((code10 && istag ("PRODUCT"))
383               || (code11 && istag ("NAME")))
384             {
385               if (s && !s->name)
386                 {
387                   /* this solvable was created without seeing a
388                      PRODUCT entry, just set the name and continue */
389                   s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
390                   continue;
391                 }
392               if (s)
393                 {
394                   /* finish old solvable */
395                   if (!s->arch)
396                     s->arch = ARCH_NOARCH;
397                   if (!s->evr)
398                     s->evr = ID_EMPTY;
399                   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
400                     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
401                   if (code10)
402                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
403                 }
404               /* create new solvable */
405               s = pool_id2solvable(pool, repo_add_solvable(repo));
406               repodata_extend(data, s - pool->solvables);
407               handle = s - pool->solvables;
408               s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
409               if (datadir)
410                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, datadir);
411               if (descrdir)
412                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
413               if (defvendor)
414                 s->vendor = pool_str2id(pool, defvendor, 1);
415               continue;
416             }
417
418           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
419              from here on.  */
420           if (!s)
421             {
422               s = pool_id2solvable(pool, repo_add_solvable(repo));
423               repodata_extend(data, s - pool->solvables);
424               handle = s - pool->solvables;
425             }
426
427           if (istag ("VERSION"))
428             pd.tmpvers = solv_strdup(value);
429           else if (istag ("RELEASE"))
430             pd.tmprel = solv_strdup(value);
431           else if (code11 && istag ("DISTRIBUTION"))
432             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
433           else if (istag ("UPDATEURLS"))
434             add_multiple_urls(data, handle, value, pool_str2id(pool, "update", 1));
435           else if (istag ("EXTRAURLS"))
436             add_multiple_urls(data, handle, value, pool_str2id(pool, "extra", 1));
437           else if (istag ("OPTIONALURLS"))
438             add_multiple_urls(data, handle, value, pool_str2id(pool, "optional", 1));
439           else if (istag ("RELNOTESURL"))
440             add_multiple_urls(data, handle, value, pool_str2id(pool, "releasenotes", 1));
441           else if (istag ("SHORTLABEL"))
442             repodata_set_str(data, s - pool->solvables, PRODUCT_SHORTLABEL, value);
443           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
444             repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, value);
445           else if (!strncmp (key, "LABEL.", 6))
446             repodata_set_str(data, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
447           else if (istag ("FLAGS"))
448             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
449           else if (istag ("VENDOR"))    /* actually already handled above */
450             s->vendor = pool_str2id(pool, value, 1);
451           else if (istag ("BASEARCHS"))
452             {
453               char *arch;
454
455               if ((arch = splitword(&value)) != 0)
456                 {
457                   s->arch = pool_str2id(pool, arch, 1);
458                   while ((arch = splitword(&value)) != 0)
459                     {
460                        otherarchs = solv_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
461                        otherarchs[numotherarchs++] = pool_str2id(pool, arch, 1);
462                     }
463                 }
464             }
465           if (!code10)
466             continue;
467
468           /*
469            * Every tag below is Code10 only
470            *
471            */
472
473           if (istag ("ARCH"))
474             /* Theoretically we want to have the best arch of the given
475                modifiers which still is compatible with the system
476                arch.  We don't know the latter here, though.  */
477             s->arch = ARCH_NOARCH;
478           else if (istag ("PREREQUIRES"))
479             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
480           else if (istag ("REQUIRES"))
481             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
482           else if (istag ("PROVIDES"))
483             s->provides = adddep(pool, &pd, s->provides, value, 0);
484           else if (istag ("CONFLICTS"))
485             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
486           else if (istag ("OBSOLETES"))
487             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
488           else if (istag ("RECOMMENDS"))
489             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
490           else if (istag ("SUGGESTS"))
491             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
492           else if (istag ("SUPPLEMENTS"))
493             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
494           else if (istag ("ENHANCES"))
495             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
496           /* FRESHENS doesn't seem to exist.  */
497           else if (istag ("TYPE"))
498             repodata_set_str(data, s - pool->solvables, PRODUCT_TYPE, value);
499
500           /* XXX do something about LINGUAS and ARCH?
501           * <ma>: Don't think so. zypp does not use or propagate them.
502           */
503 #undef istag
504         }
505       else
506         pool_debug(pool, SOLV_ERROR, "repo_content: malformed line: %s\n", line);
507     }
508
509   if (datadir)
510     free(datadir);
511   if (descrdir)
512     free(descrdir);
513   if (defvendor)
514     free(defvendor);
515
516   if (s && !s->name)
517     {
518       pool_debug(pool, SOLV_FATAL, "repo_content: 'content' incomplete, no product solvable created!\n");
519       repo_free_solvable(repo, s - pool->solvables, 1);
520       s = 0;
521     }
522   if (s)
523     {
524       if (pd.tmprel)
525         s->evr = makeevr(pool, join(&pd, pd.tmpvers, "-", pd.tmprel));
526       else
527         s->evr = makeevr(pool, pd.tmpvers);
528       pd.tmpvers = solv_free((void *)pd.tmpvers);
529       pd.tmprel = solv_free((void *)pd.tmprel);
530
531       if (!s->arch)
532         s->arch = ARCH_NOARCH;
533       if (!s->evr)
534         s->evr = ID_EMPTY;
535       if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
536         s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
537       if (code10)
538         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
539
540       /* now for every other arch, clone the product except the architecture */
541       for (i = 0; i < numotherarchs; ++i)
542         {
543           Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
544           repodata_extend(data, p - pool->solvables);
545           p->name = s->name;
546           p->evr = s->evr;
547           p->vendor = s->vendor;
548           p->arch = otherarchs[i];
549
550           /* self provides */
551           if (s->name && p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
552               p->provides = repo_addid_dep(repo, p->provides, pool_rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
553
554           /* now merge the attributes */
555           repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
556         }
557     }
558
559   if (pd.tmp)
560     solv_free(pd.tmp);
561   solv_free(line);
562   solv_free(otherarchs);
563   if (!(flags & REPO_NO_INTERNALIZE))
564     repodata_internalize(data);
565   return res;
566 }