Imported Upstream version 0.7.2
[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 <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <assert.h>
22
23 #include "pool.h"
24 #include "repo.h"
25 #include "util.h"
26 #include "chksum.h"
27 #include "repo_content.h"
28 #define DISABLE_SPLIT
29 #define DISABLE_JOIN2
30 #include "tools_util.h"
31
32 /* split off a word, return null terminated pointer to it.
33  * return NULL if there is no word left. */
34 static char *
35 splitword(char **lp)
36 {
37   char *w, *l = *lp;
38
39   while (*l == ' ' || *l == '\t')
40     l++;
41   w = *l ? l : 0;
42   while (*l && *l != ' ' && *l != '\t')
43     l++;
44   if (*l)
45     *l++ = 0;           /* terminate word */
46   while (*l == ' ' || *l == '\t')
47     l++;                /* convenience: advance to next word */
48   *lp = l;
49   return w;
50 }
51
52 struct parsedata {
53   Repo *repo;
54   char *tmp;
55   int tmpl;
56
57   const char *tmpvers;
58   const char *tmprel;
59 };
60
61 /*
62  * dependency relations
63  */
64
65 static char *flagtab[] = {
66   ">",
67   "=",
68   ">=",
69   "<",
70   "!=",
71   "<="
72 };
73
74
75 /*
76  * join up to three strings into one
77  */
78
79 static char *
80 join(struct parsedata *pd, const char *s1, const char *s2, const char *s3)
81 {
82   int l = 1;
83   char *p;
84
85   if (s1)
86     l += strlen(s1);
87   if (s2)
88     l += strlen(s2);
89   if (s3)
90     l += strlen(s3);
91   if (l > pd->tmpl)
92     {
93       pd->tmpl = l + 256;
94       pd->tmp = solv_realloc(pd->tmp, pd->tmpl);
95     }
96   p = pd->tmp;
97   if (s1)
98     {
99       strcpy(p, s1);
100       p += strlen(s1);
101     }
102   if (s2)
103     {
104       strcpy(p, s2);
105       p += strlen(s2);
106     }
107   if (s3)
108     {
109       strcpy(p, s3);
110       p += strlen(s3);
111     }
112   *p = 0;
113   return pd->tmp;
114 }
115
116
117 /*
118  * add dependency to pool
119  * OBSOLETES product:SUSE_LINUX product:openSUSE < 11.0 package:openSUSE < 11.0
120  */
121
122 static unsigned int
123 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker)
124 {
125   char *name;
126   Id id;
127
128   while ((name = splitword(&line)) != 0)
129     {
130       /* Hack, as the content file adds 'package:' for package
131          dependencies sometimes.  */
132       if (!strncmp (name, "package:", 8))
133         name += 8;
134       id = pool_str2id(pool, name, 1);
135       if (*line == '<' || *line == '>' || *line == '=') /* rel follows */
136         {
137           char *rel = splitword(&line);
138           char *evr = splitword(&line);
139           int flags;
140
141           if (!rel || !evr)
142             {
143               pool_debug(pool, SOLV_ERROR, "repo_content: bad relation '%s %s'\n", name, rel);
144               continue;
145             }
146           for (flags = 0; flags < 6; flags++)
147             if (!strcmp(rel, flagtab[flags]))
148               break;
149           if (flags == 6)
150             {
151               pool_debug(pool, SOLV_ERROR, "repo_content: unknown relation '%s'\n", rel);
152               continue;
153             }
154           id = pool_rel2id(pool, id, pool_str2id(pool, evr, 1), flags + 1, 1);
155         }
156       olddeps = repo_addid_dep(pd->repo, olddeps, id, marker);
157     }
158   return olddeps;
159 }
160
161
162 /*
163  * split value and add to pool
164  */
165
166 static void
167 add_multiple_strings(Repodata *data, Id handle, Id name, char *value)
168 {
169   char *str;
170
171   while ((str = splitword(&value)) != 0)
172     repodata_add_poolstr_array(data, handle, name, str);
173 }
174
175 /*
176  * split value and add to pool
177  */
178
179 static void
180 add_multiple_urls(Repodata *data, Id handle, char *value, Id type)
181 {
182   char *url;
183
184   while ((url = splitword(&value)) != 0)
185     {
186       repodata_add_poolstr_array(data, handle, PRODUCT_URL, url);
187       repodata_add_idarray(data, handle, PRODUCT_URL_TYPE, type);
188     }
189 }
190
191
192
193 /*
194  * add 'content' to repo
195  *
196  */
197
198 int
199 repo_add_content(Repo *repo, FILE *fp, int flags)
200 {
201   Pool *pool = repo->pool;
202   char *line, *linep;
203   int aline;
204   Solvable *s;
205   struct parsedata pd;
206   Repodata *data;
207   Id handle = 0;
208   int contentstyle = 0;
209   char *descrdir = 0;
210   char *datadir = 0;
211   char *defvendor = 0;
212
213   int i = 0;
214   int res = 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 = solv_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 = solv_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, SOLV_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           if (istag ("REPOKEYWORDS"))
292             {
293               add_multiple_strings(data, SOLVID_META, REPOSITORY_KEYWORDS, value);
294               continue;
295             }
296           if (istag ("DISTRO"))
297             {
298               Id dh = repodata_new_handle(data);
299               char *p;
300               /* like with createrepo --distro */
301               if ((p = strchr(value, ',')) != 0)
302                 {
303                   *p++ = 0;
304                   if (*value)
305                     repodata_set_poolstr(data, dh, REPOSITORY_PRODUCT_CPEID, value);
306                 }
307               else
308                 p = value;
309               if (*p)
310                 repodata_set_str(data, dh, REPOSITORY_PRODUCT_LABEL, p);
311               repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DISTROS, dh);
312               continue;
313             }
314
315           if (istag ("DESCRDIR"))
316             {
317               if (descrdir)
318                 free(descrdir);
319               else
320                 repodata_set_str(data, SOLVID_META, SUSETAGS_DESCRDIR, value);
321               if (s)
322                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, value);
323               descrdir = solv_strdup(value);
324               continue;
325             }
326           if (istag ("DATADIR"))
327             {
328               if (datadir)
329                 free(datadir);
330               else
331                 repodata_set_str(data, SOLVID_META, SUSETAGS_DATADIR, value);
332               if (s)
333                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, value);
334               datadir = solv_strdup(value);
335               continue;
336             }
337           if (istag ("VENDOR"))
338             {
339               if (defvendor)
340                 free(defvendor);
341               else
342                 repodata_set_poolstr(data, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
343               if (s)
344                 s->vendor = pool_str2id(pool, value, 1);
345               defvendor = solv_strdup(value);
346               continue;
347             }
348
349           if (istag ("META") || istag ("HASH") || istag ("KEY"))
350             {
351               char *checksumtype, *checksum;
352               Id fh, type;
353               int l;
354
355               if ((checksumtype = splitword(&value)) == 0)
356                 continue;
357               if ((checksum = splitword(&value)) == 0)
358                 continue;
359               if (!*value)
360                 continue;
361               type = solv_chksum_str2type(checksumtype);
362               if (!type)
363                 {
364                   pool_error(pool, -1, "%s: unknown checksum type '%s'", value, checksumtype);
365                   res = 1;
366                   continue;
367                 }
368               l = solv_chksum_len(type);
369               if (strlen(checksum) != 2 * l)
370                 {
371                   pool_error(pool, -1, "%s: invalid checksum length for %s", value, checksumtype);
372                   res = 1;
373                   continue;
374                 }
375               fh = repodata_new_handle(data);
376               repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
377               repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
378               repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
379               repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
380               continue;
381             }
382
383           /* product tags */
384
385           if ((code10 && istag ("PRODUCT"))
386               || (code11 && istag ("NAME")))
387             {
388               if (s && !s->name)
389                 {
390                   /* this solvable was created without seeing a
391                      PRODUCT entry, just set the name and continue */
392                   s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
393                   continue;
394                 }
395               if (s)
396                 {
397                   /* finish old solvable */
398                   if (!s->arch)
399                     s->arch = ARCH_NOARCH;
400                   if (!s->evr)
401                     s->evr = ID_EMPTY;
402                   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
403                     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
404                   if (code10)
405                     repo_rewrite_suse_deps(s, 0);
406                 }
407               /* create new solvable */
408               s = pool_id2solvable(pool, repo_add_solvable(repo));
409               handle = s - pool->solvables;
410               s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
411               if (datadir)
412                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, datadir);
413               if (descrdir)
414                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
415               if (defvendor)
416                 s->vendor = pool_str2id(pool, defvendor, 1);
417               continue;
418             }
419
420           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
421              from here on.  */
422           if (!s)
423             {
424               s = pool_id2solvable(pool, repo_add_solvable(repo));
425               handle = s - pool->solvables;
426             }
427
428           if (istag ("VERSION"))
429             pd.tmpvers = solv_strdup(value);
430           else if (istag ("RELEASE"))
431             pd.tmprel = solv_strdup(value);
432           else if (code11 && istag ("DISTRIBUTION"))
433             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
434           else if (istag ("UPDATEURLS"))
435             add_multiple_urls(data, handle, value, pool_str2id(pool, "update", 1));
436           else if (istag ("EXTRAURLS"))
437             add_multiple_urls(data, handle, value, pool_str2id(pool, "extra", 1));
438           else if (istag ("OPTIONALURLS"))
439             add_multiple_urls(data, handle, value, pool_str2id(pool, "optional", 1));
440           else if (istag ("RELNOTESURL"))
441             add_multiple_urls(data, handle, value, pool_str2id(pool, "releasenotes", 1));
442           else if (istag ("SHORTLABEL"))
443             repodata_set_str(data, s - pool->solvables, PRODUCT_SHORTLABEL, value);
444           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
445             repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, value);
446           else if (!strncmp (key, "LABEL.", 6))
447             repodata_set_str(data, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
448           else if (istag ("FLAGS"))
449             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
450           else if (istag ("VENDOR"))    /* actually already handled above */
451             s->vendor = pool_str2id(pool, value, 1);
452           else if (istag ("BASEARCHS"))
453             {
454               char *arch;
455
456               if ((arch = splitword(&value)) != 0)
457                 {
458                   s->arch = pool_str2id(pool, arch, 1);
459                   while ((arch = splitword(&value)) != 0)
460                     {
461                        otherarchs = solv_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
462                        otherarchs[numotherarchs++] = pool_str2id(pool, arch, 1);
463                     }
464                 }
465             }
466           if (!code10)
467             continue;
468
469           /*
470            * Every tag below is Code10 only
471            *
472            */
473
474           if (istag ("ARCH"))
475             /* Theoretically we want to have the best arch of the given
476                modifiers which still is compatible with the system
477                arch.  We don't know the latter here, though.  */
478             s->arch = ARCH_NOARCH;
479           else if (istag ("PREREQUIRES"))
480             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
481           else if (istag ("REQUIRES"))
482             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
483           else if (istag ("PROVIDES"))
484             s->provides = adddep(pool, &pd, s->provides, value, 0);
485           else if (istag ("CONFLICTS"))
486             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
487           else if (istag ("OBSOLETES"))
488             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
489           else if (istag ("RECOMMENDS"))
490             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
491           else if (istag ("SUGGESTS"))
492             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
493           else if (istag ("SUPPLEMENTS"))
494             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
495           else if (istag ("ENHANCES"))
496             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
497           /* FRESHENS doesn't seem to exist.  */
498           else if (istag ("TYPE"))
499             repodata_set_str(data, s - pool->solvables, PRODUCT_TYPE, value);
500
501           /* XXX do something about LINGUAS and ARCH?
502           * <ma>: Don't think so. zypp does not use or propagate them.
503           */
504 #undef istag
505         }
506       else
507         pool_debug(pool, SOLV_ERROR, "repo_content: malformed line: %s\n", line);
508     }
509
510   if (datadir)
511     free(datadir);
512   if (descrdir)
513     free(descrdir);
514   if (defvendor)
515     free(defvendor);
516
517   if (s && !s->name)
518     {
519       pool_debug(pool, SOLV_ERROR, "repo_content: 'content' incomplete, no product solvable created!\n");
520       s = solvable_free(s, 1);
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         repo_rewrite_suse_deps(s, 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           p->name = s->name;
545           p->evr = s->evr;
546           p->vendor = s->vendor;
547           p->arch = otherarchs[i];
548
549           /* self provides */
550           if (s->name && p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
551               p->provides = repo_addid_dep(repo, p->provides, pool_rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
552
553           /* now merge the attributes */
554           repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
555         }
556     }
557
558   if (pd.tmp)
559     solv_free(pd.tmp);
560   solv_free(line);
561   solv_free(otherarchs);
562   if (!(flags & REPO_NO_INTERNALIZE))
563     repodata_internalize(data);
564   return res;
565 }