5b61a6bfbb9c1864f04b0df90d2311d8ec36184e
[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
294           if (istag ("DESCRDIR"))
295             {
296               if (descrdir)
297                 free(descrdir);
298               else
299                 repodata_set_str(data, SOLVID_META, SUSETAGS_DESCRDIR, value);
300               if (s)
301                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, value);
302               descrdir = solv_strdup(value);
303               continue;
304             }
305           if (istag ("DATADIR"))
306             {
307               if (datadir)
308                 free(datadir);
309               else
310                 repodata_set_str(data, SOLVID_META, SUSETAGS_DATADIR, value);
311               if (s)
312                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, value);
313               datadir = solv_strdup(value);
314               continue;
315             }
316           if (istag ("VENDOR"))
317             {
318               if (defvendor)
319                 free(defvendor);
320               else
321                 repodata_set_poolstr(data, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
322               if (s)
323                 s->vendor = pool_str2id(pool, value, 1);
324               defvendor = solv_strdup(value);
325               continue;
326             }
327
328           if (istag ("META") || istag ("HASH") || istag ("KEY"))
329             {
330               char *checksumtype, *checksum;
331               Id fh, type;
332               int l;
333
334               if ((checksumtype = splitword(&value)) == 0)
335                 continue;
336               if ((checksum = splitword(&value)) == 0)
337                 continue;
338               if (!*value)
339                 continue;
340               type = solv_chksum_str2type(checksumtype);
341               if (!type)
342                 {
343                   pool_error(pool, -1, "%s: unknown checksum type '%s'", value, checksumtype);
344                   res = 1;
345                   continue;
346                 }
347               l = solv_chksum_len(type);
348               if (strlen(checksum) != 2 * l)
349                 {
350                   pool_error(pool, -1, "%s: invalid checksum length for %s", value, checksumtype);
351                   res = 1;
352                   continue;
353                 }
354               fh = repodata_new_handle(data);
355               repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
356               repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
357               repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
358               repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
359               continue;
360             }
361
362           /* product tags */
363
364           if ((code10 && istag ("PRODUCT"))
365               || (code11 && istag ("NAME")))
366             {
367               if (s && !s->name)
368                 {
369                   /* this solvable was created without seeing a
370                      PRODUCT entry, just set the name and continue */
371                   s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
372                   continue;
373                 }
374               if (s)
375                 {
376                   /* finish old solvable */
377                   if (!s->arch)
378                     s->arch = ARCH_NOARCH;
379                   if (!s->evr)
380                     s->evr = ID_EMPTY;
381                   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
382                     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
383                   if (code10)
384                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
385                 }
386               /* create new solvable */
387               s = pool_id2solvable(pool, repo_add_solvable(repo));
388               repodata_extend(data, s - pool->solvables);
389               handle = s - pool->solvables;
390               s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
391               if (datadir)
392                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, datadir);
393               if (descrdir)
394                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
395               if (defvendor)
396                 s->vendor = pool_str2id(pool, defvendor, 1);
397               continue;
398             }
399
400           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
401              from here on.  */
402           if (!s)
403             {
404               s = pool_id2solvable(pool, repo_add_solvable(repo));
405               repodata_extend(data, s - pool->solvables);
406               handle = s - pool->solvables;
407             }
408
409           if (istag ("VERSION"))
410             pd.tmpvers = solv_strdup(value);
411           else if (istag ("RELEASE"))
412             pd.tmprel = solv_strdup(value);
413           else if (code11 && istag ("DISTRIBUTION"))
414             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
415           else if (istag ("UPDATEURLS"))
416             add_multiple_urls(data, handle, value, pool_str2id(pool, "update", 1));
417           else if (istag ("EXTRAURLS"))
418             add_multiple_urls(data, handle, value, pool_str2id(pool, "extra", 1));
419           else if (istag ("OPTIONALURLS"))
420             add_multiple_urls(data, handle, value, pool_str2id(pool, "optional", 1));
421           else if (istag ("RELNOTESURL"))
422             add_multiple_urls(data, handle, value, pool_str2id(pool, "releasenotes", 1));
423           else if (istag ("SHORTLABEL"))
424             repodata_set_str(data, s - pool->solvables, PRODUCT_SHORTLABEL, value);
425           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
426             repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, value);
427           else if (!strncmp (key, "LABEL.", 6))
428             repodata_set_str(data, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
429           else if (istag ("FLAGS"))
430             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
431           else if (istag ("VENDOR"))    /* actually already handled above */
432             s->vendor = pool_str2id(pool, value, 1);
433           else if (istag ("BASEARCHS"))
434             {
435               char *arch;
436
437               if ((arch = splitword(&value)) != 0)
438                 {
439                   s->arch = pool_str2id(pool, arch, 1);
440                   while ((arch = splitword(&value)) != 0)
441                     {
442                        otherarchs = solv_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
443                        otherarchs[numotherarchs++] = pool_str2id(pool, arch, 1);
444                     }
445                 }
446             }
447           if (!code10)
448             continue;
449
450           /*
451            * Every tag below is Code10 only
452            *
453            */
454
455           if (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 (istag ("PREREQUIRES"))
461             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
462           else if (istag ("REQUIRES"))
463             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
464           else if (istag ("PROVIDES"))
465             s->provides = adddep(pool, &pd, s->provides, value, 0);
466           else if (istag ("CONFLICTS"))
467             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
468           else if (istag ("OBSOLETES"))
469             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
470           else if (istag ("RECOMMENDS"))
471             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
472           else if (istag ("SUGGESTS"))
473             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
474           else if (istag ("SUPPLEMENTS"))
475             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
476           else if (istag ("ENHANCES"))
477             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
478           /* FRESHENS doesn't seem to exist.  */
479           else if (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, SOLV_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, SOLV_FATAL, "repo_content: 'content' incomplete, no product solvable created!\n");
501       repo_free_solvable(repo, s - pool->solvables, 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 = solv_free((void *)pd.tmpvers);
511       pd.tmprel = solv_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     solv_free(pd.tmp);
543   solv_free(line);
544   solv_free(otherarchs);
545   if (!(flags & REPO_NO_INTERNALIZE))
546     repodata_internalize(data);
547   return res;
548 }