Imported Upstream version 0.6.15
[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_ERROR, "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_ERROR, "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 ("REPOKEYWORDS"))
294             {
295               add_multiple_strings(data, SOLVID_META, REPOSITORY_KEYWORDS, value);
296               continue;
297             }
298           if (istag ("DISTRO"))
299             {
300               Id dh = repodata_new_handle(data);
301               char *p;
302               /* like with createrepo --distro */
303               if ((p = strchr(value, ',')) != 0)
304                 {
305                   *p++ = 0;
306                   if (*value)
307                     repodata_set_poolstr(data, dh, REPOSITORY_PRODUCT_CPEID, value);
308                 }
309               else
310                 p = value;
311               if (*p)
312                 repodata_set_str(data, dh, REPOSITORY_PRODUCT_LABEL, p);
313               repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DISTROS, dh);
314               continue;
315             }
316
317           if (istag ("DESCRDIR"))
318             {
319               if (descrdir)
320                 free(descrdir);
321               else
322                 repodata_set_str(data, SOLVID_META, SUSETAGS_DESCRDIR, value);
323               if (s)
324                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, value);
325               descrdir = solv_strdup(value);
326               continue;
327             }
328           if (istag ("DATADIR"))
329             {
330               if (datadir)
331                 free(datadir);
332               else
333                 repodata_set_str(data, SOLVID_META, SUSETAGS_DATADIR, value);
334               if (s)
335                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, value);
336               datadir = solv_strdup(value);
337               continue;
338             }
339           if (istag ("VENDOR"))
340             {
341               if (defvendor)
342                 free(defvendor);
343               else
344                 repodata_set_poolstr(data, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
345               if (s)
346                 s->vendor = pool_str2id(pool, value, 1);
347               defvendor = solv_strdup(value);
348               continue;
349             }
350
351           if (istag ("META") || istag ("HASH") || istag ("KEY"))
352             {
353               char *checksumtype, *checksum;
354               Id fh, type;
355               int l;
356
357               if ((checksumtype = splitword(&value)) == 0)
358                 continue;
359               if ((checksum = splitword(&value)) == 0)
360                 continue;
361               if (!*value)
362                 continue;
363               type = solv_chksum_str2type(checksumtype);
364               if (!type)
365                 {
366                   pool_error(pool, -1, "%s: unknown checksum type '%s'", value, checksumtype);
367                   res = 1;
368                   continue;
369                 }
370               l = solv_chksum_len(type);
371               if (strlen(checksum) != 2 * l)
372                 {
373                   pool_error(pool, -1, "%s: invalid checksum length for %s", value, checksumtype);
374                   res = 1;
375                   continue;
376                 }
377               fh = repodata_new_handle(data);
378               repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
379               repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
380               repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
381               repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
382               continue;
383             }
384
385           /* product tags */
386
387           if ((code10 && istag ("PRODUCT"))
388               || (code11 && istag ("NAME")))
389             {
390               if (s && !s->name)
391                 {
392                   /* this solvable was created without seeing a
393                      PRODUCT entry, just set the name and continue */
394                   s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
395                   continue;
396                 }
397               if (s)
398                 {
399                   /* finish old solvable */
400                   if (!s->arch)
401                     s->arch = ARCH_NOARCH;
402                   if (!s->evr)
403                     s->evr = ID_EMPTY;
404                   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
405                     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
406                   if (code10)
407                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
408                 }
409               /* create new solvable */
410               s = pool_id2solvable(pool, repo_add_solvable(repo));
411               handle = s - pool->solvables;
412               s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
413               if (datadir)
414                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, datadir);
415               if (descrdir)
416                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
417               if (defvendor)
418                 s->vendor = pool_str2id(pool, defvendor, 1);
419               continue;
420             }
421
422           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
423              from here on.  */
424           if (!s)
425             {
426               s = pool_id2solvable(pool, repo_add_solvable(repo));
427               handle = s - pool->solvables;
428             }
429
430           if (istag ("VERSION"))
431             pd.tmpvers = solv_strdup(value);
432           else if (istag ("RELEASE"))
433             pd.tmprel = solv_strdup(value);
434           else if (code11 && istag ("DISTRIBUTION"))
435             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
436           else if (istag ("UPDATEURLS"))
437             add_multiple_urls(data, handle, value, pool_str2id(pool, "update", 1));
438           else if (istag ("EXTRAURLS"))
439             add_multiple_urls(data, handle, value, pool_str2id(pool, "extra", 1));
440           else if (istag ("OPTIONALURLS"))
441             add_multiple_urls(data, handle, value, pool_str2id(pool, "optional", 1));
442           else if (istag ("RELNOTESURL"))
443             add_multiple_urls(data, handle, value, pool_str2id(pool, "releasenotes", 1));
444           else if (istag ("SHORTLABEL"))
445             repodata_set_str(data, s - pool->solvables, PRODUCT_SHORTLABEL, value);
446           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
447             repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, value);
448           else if (!strncmp (key, "LABEL.", 6))
449             repodata_set_str(data, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
450           else if (istag ("FLAGS"))
451             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
452           else if (istag ("VENDOR"))    /* actually already handled above */
453             s->vendor = pool_str2id(pool, value, 1);
454           else if (istag ("BASEARCHS"))
455             {
456               char *arch;
457
458               if ((arch = splitword(&value)) != 0)
459                 {
460                   s->arch = pool_str2id(pool, arch, 1);
461                   while ((arch = splitword(&value)) != 0)
462                     {
463                        otherarchs = solv_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
464                        otherarchs[numotherarchs++] = pool_str2id(pool, arch, 1);
465                     }
466                 }
467             }
468           if (!code10)
469             continue;
470
471           /*
472            * Every tag below is Code10 only
473            *
474            */
475
476           if (istag ("ARCH"))
477             /* Theoretically we want to have the best arch of the given
478                modifiers which still is compatible with the system
479                arch.  We don't know the latter here, though.  */
480             s->arch = ARCH_NOARCH;
481           else if (istag ("PREREQUIRES"))
482             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
483           else if (istag ("REQUIRES"))
484             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
485           else if (istag ("PROVIDES"))
486             s->provides = adddep(pool, &pd, s->provides, value, 0);
487           else if (istag ("CONFLICTS"))
488             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
489           else if (istag ("OBSOLETES"))
490             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
491           else if (istag ("RECOMMENDS"))
492             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
493           else if (istag ("SUGGESTS"))
494             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
495           else if (istag ("SUPPLEMENTS"))
496             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
497           else if (istag ("ENHANCES"))
498             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
499           /* FRESHENS doesn't seem to exist.  */
500           else if (istag ("TYPE"))
501             repodata_set_str(data, s - pool->solvables, PRODUCT_TYPE, value);
502
503           /* XXX do something about LINGUAS and ARCH?
504           * <ma>: Don't think so. zypp does not use or propagate them.
505           */
506 #undef istag
507         }
508       else
509         pool_debug(pool, SOLV_ERROR, "repo_content: malformed line: %s\n", line);
510     }
511
512   if (datadir)
513     free(datadir);
514   if (descrdir)
515     free(descrdir);
516   if (defvendor)
517     free(defvendor);
518
519   if (s && !s->name)
520     {
521       pool_debug(pool, SOLV_ERROR, "repo_content: 'content' incomplete, no product solvable created!\n");
522       repo_free_solvable(repo, s - pool->solvables, 1);
523       s = 0;
524     }
525   if (s)
526     {
527       if (pd.tmprel)
528         s->evr = makeevr(pool, join(&pd, pd.tmpvers, "-", pd.tmprel));
529       else
530         s->evr = makeevr(pool, pd.tmpvers);
531       pd.tmpvers = solv_free((void *)pd.tmpvers);
532       pd.tmprel = solv_free((void *)pd.tmprel);
533
534       if (!s->arch)
535         s->arch = ARCH_NOARCH;
536       if (!s->evr)
537         s->evr = ID_EMPTY;
538       if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
539         s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
540       if (code10)
541         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
542
543       /* now for every other arch, clone the product except the architecture */
544       for (i = 0; i < numotherarchs; ++i)
545         {
546           Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
547           p->name = s->name;
548           p->evr = s->evr;
549           p->vendor = s->vendor;
550           p->arch = otherarchs[i];
551
552           /* self provides */
553           if (s->name && p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
554               p->provides = repo_addid_dep(repo, p->provides, pool_rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
555
556           /* now merge the attributes */
557           repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
558         }
559     }
560
561   if (pd.tmp)
562     solv_free(pd.tmp);
563   solv_free(line);
564   solv_free(otherarchs);
565   if (!(flags & REPO_NO_INTERNALIZE))
566     repodata_internalize(data);
567   return res;
568 }