- don't abort on unknown elements
[platform/upstream/libsolv.git] / tools / repo_content.c
1 /*
2  * repo_content.c
3  *
4  * Parses 'content' file into .solv
5  * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata/content for a description
6  * of the syntax
7  *
8  *
9  * Copyright (c) 2007, Novell Inc.
10  *
11  * This program is licensed under the BSD license, read LICENSE.BSD
12  * for further information
13  */
14
15 #include <sys/types.h>
16 #include <limits.h>
17 #include <fcntl.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 "repo_content.h"
27
28
29 /* split off a word, return null terminated pointer to it.
30  * return NULL if there is no word left. */
31 static char *
32 splitword(char **lp)
33 {
34   char *w, *l = *lp;
35
36   while (*l == ' ' || *l == '\t')
37     l++;
38   w = *l ? l : 0;
39   while (*l && *l != ' ' && *l != '\t')
40     l++;
41   if (*l)
42     *l++ = 0;           /* terminate word */
43   while (*l == ' ' || *l == '\t')
44     l++;                /* convenience: advance to next word */
45   *lp = l;
46   return w;
47 }
48
49 struct parsedata {
50   Repo *repo;
51   char *tmp;
52   int tmpl;
53 };
54
55
56 static Id
57 makeevr(Pool *pool, char *s)
58 {
59   if (!strncmp(s, "0:", 2) && s[2])
60     s += 2;
61   return str2id(pool, s, 1);
62 }
63
64 /*
65  * dependency relations
66  */
67
68 static char *flagtab[] = {
69   ">",
70   "=",
71   ">=",
72   "<",
73   "!=",
74   "<="
75 };
76
77
78 /*
79  * join up to three strings into one
80  */
81
82 static char *
83 join(struct parsedata *pd, const char *s1, const char *s2, const char *s3)
84 {
85   int l = 1;
86   char *p;
87
88   if (s1)
89     l += strlen(s1);
90   if (s2)
91     l += strlen(s2);
92   if (s3)
93     l += strlen(s3);
94   if (l > pd->tmpl)
95     {
96       pd->tmpl = l + 256;
97       pd->tmp = sat_realloc(pd->tmp, pd->tmpl);
98     }
99   p = pd->tmp;
100   if (s1)
101     {
102       strcpy(p, s1);
103       p += strlen(s1);
104     }
105   if (s2)
106     {
107       strcpy(p, s2);
108       p += strlen(s2);
109     }
110   if (s3)
111     {
112       strcpy(p, s3);
113       p += strlen(s3);
114     }
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 = 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               fprintf(stderr, "bad relation '%s %s'\n", name, rel);
146               exit(1);
147             }
148           for (flags = 0; flags < 6; flags++)
149             if (!strcmp(rel, flagtab[flags]))
150               break;
151           if (flags == 6)
152             {
153               fprintf(stderr, "Unknown relation '%s'\n", rel);
154               exit(1);
155             }
156           id = rel2id(pool, id, 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 void
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
212   int i = 0;
213
214   /* architectures
215      we use the first architecture in BASEARCHS or noarch
216      for the product. At the end we create (clone) the product
217      for each one of the remaining architectures
218      we allow max 4 archs
219   */
220   unsigned int numotherarchs = 0;
221   Id *otherarchs = 0;
222
223   if (!(flags & REPO_REUSE_REPODATA))
224     data = repo_add_repodata(repo, 0);
225   else
226     data = repo_last_repodata(repo);
227
228   memset(&pd, 0, sizeof(pd));
229   line = sat_malloc(1024);
230   aline = 1024;
231
232   if (repo->nrepodata)
233     /* use last repodata */
234     data = repo->repodata + repo->nrepodata - 1;
235   else
236     data = repo_add_repodata(repo, 0);
237
238   pd.repo = repo;
239   linep = line;
240   s = 0;
241
242   for (;;)
243     {
244       char *key, *value;
245
246       /* read line into big-enough buffer */
247       if (linep - line + 16 > aline)
248         {
249           aline = linep - line;
250           line = sat_realloc(line, aline + 512);
251           linep = line + aline;
252           aline += 512;
253         }
254       if (!fgets(linep, aline - (linep - line), fp))
255         break;
256       linep += strlen(linep);
257       if (linep == line || linep[-1] != '\n')
258         continue;
259       *--linep = 0;
260       linep = line;
261
262       /* expect "key value" lines */
263       value = line;
264       key = splitword(&value);
265
266       if (key)
267         {
268 #if 0
269           fprintf (stderr, "key %s, value %s\n", key, value);
270 #endif
271
272 #define istag(x) (!strcmp (key, x))
273 #define code10 (contentstyle == 10)
274 #define code11 (contentstyle == 11)
275
276           if (contentstyle == 0)
277             {
278               if (istag ("CONTENTSTYLE"))
279                 {
280                   contentstyle = atoi(value);
281                   continue;
282                 }
283               else
284                 contentstyle = 10;
285             }
286
287           if ((code10 && istag ("PRODUCT"))
288               || (code11 && istag ("NAME")))
289             {
290               if (s && !s->name)
291                 {
292                   /* this solvable was created without seeing a
293                      PRODUCT entry, just set the name and continue */
294                   s->name = str2id(pool, join(&pd, "product", ":", value), 1);
295                   continue;
296                 }
297               if (s)
298                 {
299                   /* finish old solvable */
300                   if (!s->arch)
301                     s->arch = ARCH_NOARCH;
302                   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
303                     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
304                   if (code10)
305                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
306                 }
307               /* create new solvable */
308               s = pool_id2solvable(pool, repo_add_solvable(repo));
309               repodata_extend(data, s - pool->solvables);
310               handle = s - pool->solvables;
311               s->name = str2id(pool, join(&pd, "product", ":", value), 1);
312               continue;
313             }
314
315           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
316              from here on.  */
317           if (!s)
318             {
319               s = pool_id2solvable(pool, repo_add_solvable(repo));
320               repodata_extend(data, s - pool->solvables);
321               handle = s - pool->solvables;
322             }
323
324           if (istag ("VERSION"))
325             s->evr = makeevr(pool, value);
326           else if (code11 && istag ("DISTRIBUTION"))
327             repo_set_str(repo, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
328           else if (istag ("DATADIR"))
329             repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, value);
330           else if (istag ("UPDATEURLS"))
331             add_multiple_urls(data, handle, value, str2id(pool, "update", 1));
332           else if (istag ("EXTRAURLS"))
333             add_multiple_urls(data, handle, value, str2id(pool, "extra", 1));
334           else if (istag ("OPTIONALURLS"))
335             add_multiple_urls(data, handle, value, str2id(pool, "optional", 1));
336           else if (istag ("RELNOTESURL"))
337             add_multiple_urls(data, handle, value, str2id(pool, "releasenotes", 1));
338           else if (istag ("SHORTLABEL"))
339             repo_set_str(repo, s - pool->solvables, PRODUCT_SHORTLABEL, value);
340           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
341             repo_set_str(repo, s - pool->solvables, SOLVABLE_SUMMARY, value);
342           else if (!strncmp (key, "LABEL.", 6))
343             repo_set_str(repo, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
344           else if (istag ("FLAGS"))
345             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
346           else if (istag ("VENDOR"))
347             s->vendor = str2id(pool, value, 1);
348           else if (istag ("BASEARCHS"))
349             {
350               char *arch;
351
352               if ((arch = splitword(&value)) != 0)
353                 {
354                   s->arch = str2id(pool, arch, 1);
355                   while ((arch = splitword(&value)) != 0)
356                     {
357                        otherarchs = sat_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
358                        otherarchs[numotherarchs++] = str2id(pool, arch, 1);
359                     }
360                 }
361             }
362
363           /*
364            * Every tag below is Code10 only
365            *
366            */
367
368           else if (code10 && istag ("DISTPRODUCT"))
369             /* DISTPRODUCT is for registration and Yast, not for the solver. */
370             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTPRODUCT, value);
371           else if (code10 && istag ("DISTVERSION"))
372             /* DISTVERSION is for registration and Yast, not for the solver. */
373             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTVERSION, value);
374           else if (code10 && istag ("ARCH"))
375             /* Theoretically we want to have the best arch of the given
376                modifiers which still is compatible with the system
377                arch.  We don't know the latter here, though.  */
378             s->arch = ARCH_NOARCH;
379           else if (code10 && istag ("PREREQUIRES"))
380             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
381           else if (code10 && istag ("REQUIRES"))
382             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
383           else if (code10 && istag ("PROVIDES"))
384             s->provides = adddep(pool, &pd, s->provides, value, 0);
385           else if (code10 && istag ("CONFLICTS"))
386             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
387           else if (code10 && istag ("OBSOLETES"))
388             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
389           else if (code10 && istag ("RECOMMENDS"))
390             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
391           else if (code10 && istag ("SUGGESTS"))
392             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
393           else if (code10 && istag ("SUPPLEMENTS"))
394             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
395           else if (code10 && istag ("ENHANCES"))
396             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
397           /* FRESHENS doesn't seem to exist.  */
398           else if (code10 && istag ("TYPE"))
399             repo_set_str(repo, s - pool->solvables, PRODUCT_TYPE, value);
400
401           /* XXX do something about LINGUAS and ARCH?
402           * <ma>: Don't think so. zypp does not use or propagate them.
403           */
404 #undef istag
405         }
406       else
407         fprintf (stderr, "malformed line: %s\n", line);
408     }
409
410   if (!s || !s->name)
411     {
412       fprintf(stderr, "No product solvable created !\n");
413       exit(1);
414     }
415
416   if (!s->arch)
417     s->arch = ARCH_NOARCH;
418   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
419     {
420       s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
421       if (code10)
422         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
423     }
424
425   /* now for every other arch, clone the product except the architecture */
426   for (i = 0; i < numotherarchs; ++i)
427     {
428       Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
429       repodata_extend(data, p - pool->solvables);
430       p->name = s->name;
431       p->evr = s->evr;
432       p->vendor = s->vendor;
433       p->arch = otherarchs[i];
434
435       /* self provides */
436       if (p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
437           p->provides = repo_addid_dep(repo, p->provides, rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
438
439       /* now merge the attributes */
440       repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
441     }
442
443   if (pd.tmp)
444     sat_free(pd.tmp);
445   sat_free(line);
446   sat_free(otherarchs);
447   if (!(flags & REPO_NO_INTERNALIZE))
448     repodata_internalize(data);
449 }