re-add parsing of /etc/<xyz>-release (got lost in transistion from
[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  */
122
123 static unsigned int
124 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker)
125 {
126   char *name;
127   Id id;
128   
129   while ((name = splitword(&line)) != 0)
130     {
131       /* Hack, as the content file adds 'package:' for package
132          dependencies sometimes.  */
133       if (!strncmp (name, "package:", 8))
134         name += 8;
135       id = str2id(pool, name, 1);
136       if (strpbrk(line, "<>="))
137         {
138           char *rel = splitword(&line);
139           char *evr = splitword(&line);
140           int flags;
141
142           if (!rel || !evr)
143             {
144               fprintf(stderr, "bad relation '%s %s'\n", name, rel);
145               exit(1);
146             }
147           for (flags = 0; flags < 6; flags++)
148             if (!strcmp(rel, flagtab[flags]))
149               break;
150           if (flags == 6)
151             {
152               fprintf(stderr, "Unknown relation '%s'\n", rel);
153               exit(1);
154             }
155           id = rel2id(pool, id, str2id(pool, evr, 1), flags + 1, 1);
156         }
157       olddeps = repo_addid_dep(pd->repo, olddeps, id, marker);
158     }
159   return olddeps;
160 }
161
162
163 /*
164  * split value and add to pool
165  */
166
167 static void
168 add_multiple_strings(Repodata *data, Id handle, Id name, char *value)
169 {
170   char *str;
171
172   while ((str = splitword(&value)) != 0)
173     repodata_add_poolstr_array(data, handle, name, str);
174 }
175
176 /*
177  * split value and add to pool
178  */
179
180 static void
181 add_multiple_urls(Repodata *data, Id handle, char *value, Id type)
182 {
183   char *url;
184
185   while ((url = splitword(&value)) != 0)
186     {
187       repodata_add_poolstr_array(data, handle, PRODUCT_URL, url);
188       repodata_add_idarray(data, handle, PRODUCT_URL_TYPE, type);
189     }
190 }
191
192
193
194 /*
195  * add 'content' to repo
196  *
197  */
198
199 void
200 repo_add_content(Repo *repo, FILE *fp)
201 {
202   Pool *pool = repo->pool;
203   char *line, *linep;
204   int aline;
205   Solvable *s;
206   struct parsedata pd;
207   Repodata *data;
208   Id handle = 0;
209   int contentstyle = 0;
210
211   int i = 0;
212
213   /* architectures
214      we use the first architecture in BASEARCHS or noarch
215      for the product. At the end we create (clone) the product
216      for each one of the remaining architectures
217      we allow max 4 archs 
218   */
219   unsigned int numotherarchs = 0;
220   Id *otherarchs = 0;
221   
222   memset(&pd, 0, sizeof(pd));
223   line = sat_malloc(1024);
224   aline = 1024;
225
226   if (repo->nrepodata)
227     /* use last repodata */
228     data = repo->repodata + repo->nrepodata - 1;
229   else
230     data = repo_add_repodata(repo, 0);
231
232   pd.repo = repo;
233   linep = line;
234   s = 0;
235
236   for (;;)
237     {
238       char *key, *value;
239
240       /* read line into big-enough buffer */
241       if (linep - line + 16 > aline)
242         {
243           aline = linep - line;
244           line = sat_realloc(line, aline + 512);
245           linep = line + aline;
246           aline += 512;
247         }
248       if (!fgets(linep, aline - (linep - line), fp))
249         break;
250       linep += strlen(linep);
251       if (linep == line || linep[-1] != '\n')
252         continue;
253       *--linep = 0;
254       linep = line;
255
256       /* expect "key value" lines */
257       value = line;
258       key = splitword(&value);
259       
260       if (key)
261         {
262 #if 0
263           fprintf (stderr, "key %s, value %s\n", key, value);
264 #endif
265
266 #define istag(x) (!strcmp (key, x))
267 #define code10 (contentstyle == 10)
268 #define code11 (contentstyle == 11)
269
270           if (contentstyle == 0)
271             {
272               if (istag ("CONTENTSTYLE"))
273                 {
274                   contentstyle = atoi(value);
275                   continue;
276                 }
277               else
278                 contentstyle = 10;
279             }
280
281           if ((code10 && istag ("PRODUCT"))
282               || (code11 && istag ("NAME")))
283             {
284               if (s && !s->name)
285                 {
286                   /* this solvable was created without seeing a
287                      PRODUCT entry, just set the name and continue */
288                   s->name = str2id(pool, join(&pd, "product", ":", value), 1);
289                   continue;
290                 }
291               if (s)
292                 {
293                   /* finish old solvable */
294                   if (!s->arch)
295                     s->arch = ARCH_NOARCH;
296                   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
297                     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
298                   if (code10)
299                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
300                 }
301               /* create new solvable */
302               s = pool_id2solvable(pool, repo_add_solvable(repo));
303               repodata_extend(data, s - pool->solvables);
304               handle = repodata_get_handle(data, s - pool->solvables - repo->start);
305               s->name = str2id(pool, join(&pd, "product", ":", value), 1);
306               continue;
307             }
308
309           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
310              from here on.  */
311           if (!s)
312             {
313               s = pool_id2solvable(pool, repo_add_solvable(repo));
314               repodata_extend(data, s - pool->solvables);
315               handle = repodata_get_handle(data, s - pool->solvables - repo->start);
316             }
317
318           if (code11 && istag ("REFERENCES"))
319             repo_set_id(repo, s - pool->solvables, PRODUCT_REFERENCES, str2id(pool, value, 1));
320           else if (istag ("VERSION"))
321             s->evr = makeevr(pool, value);
322           else if (code11 && istag ("DISTRIBUTION"))
323             repo_set_str(repo, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
324           else if (code11 && istag ("FLAVOR"))
325             repo_set_str(repo, s - pool->solvables, PRODUCT_FLAVOR, value);
326           else if (istag ("DATADIR"))
327             repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, value);
328           else if (istag ("UPDATEURLS"))
329             add_multiple_urls(data, handle, value, str2id(pool, "update", 1));
330           else if (istag ("EXTRAURLS"))
331             add_multiple_urls(data, handle, value, str2id(pool, "extra", 1));
332           else if (istag ("OPTIONALURLS"))
333             add_multiple_urls(data, handle, value, str2id(pool, "optional", 1));
334           else if (istag ("RELNOTESURL"))
335             add_multiple_urls(data, handle, value, str2id(pool, "releasenotes", 1));
336           else if (istag ("SHORTLABEL"))
337             repo_set_str(repo, s - pool->solvables, PRODUCT_SHORTLABEL, value);
338           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
339             repo_set_str(repo, s - pool->solvables, SOLVABLE_SUMMARY, value);
340           else if (!strncmp (key, "LABEL.", 6))
341             repo_set_str(repo, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
342           else if (istag ("FLAGS"))
343             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
344           else if (istag ("VENDOR"))
345             s->vendor = str2id(pool, value, 1);
346           else if (istag ("BASEARCHS"))
347             {
348               char *arch;
349
350               if ((arch = splitword(&value)) != 0)
351                 {
352                   s->arch = str2id(pool, arch, 1);
353                   while ((arch = splitword(&value)) != 0)
354                     {
355                        otherarchs = sat_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
356                        otherarchs[numotherarchs++] = str2id(pool, arch, 1);
357                     }
358                 }
359             }
360
361           /*
362            * Every tag below is Code10 only
363            *
364            */
365
366           else if (code10 && istag ("DISTPRODUCT"))
367             /* DISTPRODUCT is for registration and Yast, not for the solver. */
368             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTPRODUCT, value);
369           else if (code10 && istag ("DISTVERSION"))
370             /* DISTVERSION is for registration and Yast, not for the solver. */
371             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTVERSION, value);
372           else if (code10 && istag ("ARCH"))
373             /* Theoretically we want to have the best arch of the given
374                modifiers which still is compatible with the system
375                arch.  We don't know the latter here, though.  */
376             s->arch = ARCH_NOARCH;
377           else if (code10 && istag ("PREREQUIRES"))
378             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
379           else if (code10 && istag ("REQUIRES"))
380             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
381           else if (code10 && istag ("PROVIDES"))
382             s->provides = adddep(pool, &pd, s->provides, value, 0);
383           else if (code10 && istag ("CONFLICTS"))
384             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
385           else if (code10 && istag ("OBSOLETES"))
386             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
387           else if (code10 && istag ("RECOMMENDS"))
388             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
389           else if (code10 && istag ("SUGGESTS"))
390             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
391           else if (code10 && istag ("SUPPLEMENTS"))
392             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
393           else if (code10 && istag ("ENHANCES"))
394             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
395           /* FRESHENS doesn't seem to exist.  */
396           else if (code10 && istag ("TYPE"))
397             repo_set_str(repo, s - pool->solvables, PRODUCT_TYPE, value);
398
399           /* XXX do something about LINGUAS and ARCH?
400           * <ma>: Don't think so. zypp does not use or propagate them.
401           */
402 #undef istag
403         }
404       else
405         fprintf (stderr, "malformed line: %s\n", line);
406     }
407
408   if (!s || !s->name)
409     {
410       fprintf(stderr, "No product solvable created !\n");
411       exit(1);
412     }
413
414   if (!s->arch)
415     s->arch = ARCH_NOARCH;
416   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
417     {
418       s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
419       if (code10)
420         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
421     }
422   
423   /* now for every other arch, clone the product except the architecture */
424   for (i = 0; i < numotherarchs; ++i)
425     {
426       Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
427       repodata_extend(data, p - pool->solvables);
428       /*handle = repodata_get_handle(data, p - pool->solvables - repo->start);*/
429       p->name = s->name;
430       p->evr = s->evr;
431       p->vendor = s->vendor;
432       p->arch = otherarchs[i];
433
434       /* self provides */
435       if (p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
436           p->provides = repo_addid_dep(repo, p->provides, rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
437
438       /* now merge the attributes */
439       repodata_merge_attrs(data, p - pool->solvables - repo->start, s - pool->solvables- repo->start);
440     }
441   
442   if (data)
443     repodata_internalize(data);
444
445   if (pd.tmp)
446     sat_free(pd.tmp);
447   sat_free(line);
448   sat_free(otherarchs);
449 }