- Product arttributes: removed FLAVOR and REFERENCES, added PRODUCTLINE.
[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 (strpbrk(line, "<>=") == line) /* next(!) word is rel */
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)
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   memset(&pd, 0, sizeof(pd));
224   line = sat_malloc(1024);
225   aline = 1024;
226
227   if (repo->nrepodata)
228     /* use last repodata */
229     data = repo->repodata + repo->nrepodata - 1;
230   else
231     data = repo_add_repodata(repo, 0);
232
233   pd.repo = repo;
234   linep = line;
235   s = 0;
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 = sat_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       *--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           if (contentstyle == 0)
272             {
273               if (istag ("CONTENTSTYLE"))
274                 {
275                   contentstyle = atoi(value);
276                   continue;
277                 }
278               else
279                 contentstyle = 10;
280             }
281
282           if ((code10 && istag ("PRODUCT"))
283               || (code11 && istag ("NAME")))
284             {
285               if (s && !s->name)
286                 {
287                   /* this solvable was created without seeing a
288                      PRODUCT entry, just set the name and continue */
289                   s->name = str2id(pool, join(&pd, "product", ":", value), 1);
290                   continue;
291                 }
292               if (s)
293                 {
294                   /* finish old solvable */
295                   if (!s->arch)
296                     s->arch = ARCH_NOARCH;
297                   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
298                     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
299                   if (code10)
300                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
301                 }
302               /* create new solvable */
303               s = pool_id2solvable(pool, repo_add_solvable(repo));
304               repodata_extend(data, s - pool->solvables);
305               handle = repodata_get_handle(data, s - pool->solvables - repo->start);
306               s->name = str2id(pool, join(&pd, "product", ":", value), 1);
307               continue;
308             }
309
310           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
311              from here on.  */
312           if (!s)
313             {
314               s = pool_id2solvable(pool, repo_add_solvable(repo));
315               repodata_extend(data, s - pool->solvables);
316               handle = repodata_get_handle(data, s - pool->solvables - repo->start);
317             }
318
319           if (istag ("VERSION"))
320             s->evr = makeevr(pool, value);
321           else if (code11 && istag ("DISTRIBUTION"))
322             repo_set_str(repo, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
323           else if (istag ("DATADIR"))
324             repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, value);
325           else if (istag ("UPDATEURLS"))
326             add_multiple_urls(data, handle, value, str2id(pool, "update", 1));
327           else if (istag ("EXTRAURLS"))
328             add_multiple_urls(data, handle, value, str2id(pool, "extra", 1));
329           else if (istag ("OPTIONALURLS"))
330             add_multiple_urls(data, handle, value, str2id(pool, "optional", 1));
331           else if (istag ("RELNOTESURL"))
332             add_multiple_urls(data, handle, value, str2id(pool, "releasenotes", 1));
333           else if (istag ("SHORTLABEL"))
334             repo_set_str(repo, s - pool->solvables, PRODUCT_SHORTLABEL, value);
335           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
336             repo_set_str(repo, s - pool->solvables, SOLVABLE_SUMMARY, value);
337           else if (!strncmp (key, "LABEL.", 6))
338             repo_set_str(repo, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
339           else if (istag ("FLAGS"))
340             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
341           else if (istag ("VENDOR"))
342             s->vendor = str2id(pool, value, 1);
343           else if (istag ("BASEARCHS"))
344             {
345               char *arch;
346
347               if ((arch = splitword(&value)) != 0)
348                 {
349                   s->arch = str2id(pool, arch, 1);
350                   while ((arch = splitword(&value)) != 0)
351                     {
352                        otherarchs = sat_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
353                        otherarchs[numotherarchs++] = str2id(pool, arch, 1);
354                     }
355                 }
356             }
357
358           /*
359            * Every tag below is Code10 only
360            *
361            */
362
363           else if (code10 && istag ("DISTPRODUCT"))
364             /* DISTPRODUCT is for registration and Yast, not for the solver. */
365             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTPRODUCT, value);
366           else if (code10 && istag ("DISTVERSION"))
367             /* DISTVERSION is for registration and Yast, not for the solver. */
368             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTVERSION, value);
369           else if (code10 && istag ("ARCH"))
370             /* Theoretically we want to have the best arch of the given
371                modifiers which still is compatible with the system
372                arch.  We don't know the latter here, though.  */
373             s->arch = ARCH_NOARCH;
374           else if (code10 && istag ("PREREQUIRES"))
375             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
376           else if (code10 && istag ("REQUIRES"))
377             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
378           else if (code10 && istag ("PROVIDES"))
379             s->provides = adddep(pool, &pd, s->provides, value, 0);
380           else if (code10 && istag ("CONFLICTS"))
381             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
382           else if (code10 && istag ("OBSOLETES"))
383             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
384           else if (code10 && istag ("RECOMMENDS"))
385             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
386           else if (code10 && istag ("SUGGESTS"))
387             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
388           else if (code10 && istag ("SUPPLEMENTS"))
389             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
390           else if (code10 && istag ("ENHANCES"))
391             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
392           /* FRESHENS doesn't seem to exist.  */
393           else if (code10 && istag ("TYPE"))
394             repo_set_str(repo, s - pool->solvables, PRODUCT_TYPE, value);
395
396           /* XXX do something about LINGUAS and ARCH?
397           * <ma>: Don't think so. zypp does not use or propagate them.
398           */
399 #undef istag
400         }
401       else
402         fprintf (stderr, "malformed line: %s\n", line);
403     }
404
405   if (!s || !s->name)
406     {
407       fprintf(stderr, "No product solvable created !\n");
408       exit(1);
409     }
410
411   if (!s->arch)
412     s->arch = ARCH_NOARCH;
413   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
414     {
415       s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
416       if (code10)
417         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
418     }
419
420   /* now for every other arch, clone the product except the architecture */
421   for (i = 0; i < numotherarchs; ++i)
422     {
423       Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
424       repodata_extend(data, p - pool->solvables);
425       /*handle = repodata_get_handle(data, p - pool->solvables - repo->start);*/
426       p->name = s->name;
427       p->evr = s->evr;
428       p->vendor = s->vendor;
429       p->arch = otherarchs[i];
430
431       /* self provides */
432       if (p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
433           p->provides = repo_addid_dep(repo, p->provides, rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
434
435       /* now merge the attributes */
436       repodata_merge_attrs(data, p - pool->solvables - repo->start, s - pool->solvables- repo->start);
437     }
438
439   if (data)
440     repodata_internalize(data);
441
442   if (pd.tmp)
443     sat_free(pd.tmp);
444   sat_free(line);
445   sat_free(otherarchs);
446 }