Merge branch 'master' of git@git.opensuse.org:projects/zypp/sat-solver
[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 #define DISABLE_SPLIT
28 #include "tools_util.h"
29
30 /* split off a word, return null terminated pointer to it.
31  * return NULL if there is no word left. */
32 static char *
33 splitword(char **lp)
34 {
35   char *w, *l = *lp;
36
37   while (*l == ' ' || *l == '\t')
38     l++;
39   w = *l ? l : 0;
40   while (*l && *l != ' ' && *l != '\t')
41     l++;
42   if (*l)
43     *l++ = 0;           /* terminate word */
44   while (*l == ' ' || *l == '\t')
45     l++;                /* convenience: advance to next word */
46   *lp = l;
47   return w;
48 }
49
50 struct parsedata {
51   Repo *repo;
52   char *tmp;
53   int tmpl;
54
55   const char *tmpvers;
56   const char *tmprel;
57 };
58
59 /*
60  * dependency relations
61  */
62
63 static char *flagtab[] = {
64   ">",
65   "=",
66   ">=",
67   "<",
68   "!=",
69   "<="
70 };
71
72
73 /*
74  * join up to three strings into one
75  */
76
77 static char *
78 join(struct parsedata *pd, const char *s1, const char *s2, const char *s3)
79 {
80   int l = 1;
81   char *p;
82
83   if (s1)
84     l += strlen(s1);
85   if (s2)
86     l += strlen(s2);
87   if (s3)
88     l += strlen(s3);
89   if (l > pd->tmpl)
90     {
91       pd->tmpl = l + 256;
92       pd->tmp = sat_realloc(pd->tmp, pd->tmpl);
93     }
94   p = pd->tmp;
95   if (s1)
96     {
97       strcpy(p, s1);
98       p += strlen(s1);
99     }
100   if (s2)
101     {
102       strcpy(p, s2);
103       p += strlen(s2);
104     }
105   if (s3)
106     {
107       strcpy(p, s3);
108       p += strlen(s3);
109     }
110   return pd->tmp;
111 }
112
113
114 /*
115  * add dependency to pool
116  * OBSOLETES product:SUSE_LINUX product:openSUSE < 11.0 package:openSUSE < 11.0
117  */
118
119 static unsigned int
120 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker)
121 {
122   char *name;
123   Id id;
124
125   while ((name = splitword(&line)) != 0)
126     {
127       /* Hack, as the content file adds 'package:' for package
128          dependencies sometimes.  */
129       if (!strncmp (name, "package:", 8))
130         name += 8;
131       id = str2id(pool, name, 1);
132       if (*line == '<' || *line == '>' || *line == '=') /* rel follows */
133         {
134           char *rel = splitword(&line);
135           char *evr = splitword(&line);
136           int flags;
137
138           if (!rel || !evr)
139             {
140               pool_debug(pool, SAT_FATAL, "repo_content: bad relation '%s %s'\n", name, rel);
141               exit(1);
142             }
143           for (flags = 0; flags < 6; flags++)
144             if (!strcmp(rel, flagtab[flags]))
145               break;
146           if (flags == 6)
147             {
148               pool_debug(pool, SAT_FATAL, "repo_content: unknown relation '%s'\n", rel);
149               exit(1);
150             }
151           id = rel2id(pool, id, str2id(pool, evr, 1), flags + 1, 1);
152         }
153       olddeps = repo_addid_dep(pd->repo, olddeps, id, marker);
154     }
155   return olddeps;
156 }
157
158
159 /*
160  * split value and add to pool
161  */
162
163 static void
164 add_multiple_strings(Repodata *data, Id handle, Id name, char *value)
165 {
166   char *str;
167
168   while ((str = splitword(&value)) != 0)
169     repodata_add_poolstr_array(data, handle, name, str);
170 }
171
172 /*
173  * split value and add to pool
174  */
175
176 static void
177 add_multiple_urls(Repodata *data, Id handle, char *value, Id type)
178 {
179   char *url;
180
181   while ((url = splitword(&value)) != 0)
182     {
183       repodata_add_poolstr_array(data, handle, PRODUCT_URL, url);
184       repodata_add_idarray(data, handle, PRODUCT_URL_TYPE, type);
185     }
186 }
187
188
189
190 /*
191  * add 'content' to repo
192  *
193  */
194
195 void
196 repo_add_content(Repo *repo, FILE *fp, int flags)
197 {
198   Pool *pool = repo->pool;
199   char *line, *linep;
200   int aline;
201   Solvable *s;
202   struct parsedata pd;
203   Repodata *data;
204   Id handle = 0;
205   int contentstyle = 0;
206
207   int i = 0;
208
209   /* architectures
210      we use the first architecture in BASEARCHS or noarch
211      for the product. At the end we create (clone) the product
212      for each one of the remaining architectures
213      we allow max 4 archs
214   */
215   unsigned int numotherarchs = 0;
216   Id *otherarchs = 0;
217
218   if (!(flags & REPO_REUSE_REPODATA))
219     data = repo_add_repodata(repo, 0);
220   else
221     data = repo_last_repodata(repo);
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       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           if (contentstyle == 0)
274             {
275               if (istag ("CONTENTSTYLE"))
276                 {
277                   contentstyle = atoi(value);
278                   continue;
279                 }
280               else
281                 contentstyle = 10;
282             }
283
284           if ((code10 && istag ("PRODUCT"))
285               || (code11 && istag ("NAME")))
286             {
287               if (s && !s->name)
288                 {
289                   /* this solvable was created without seeing a
290                      PRODUCT entry, just set the name and continue */
291                   s->name = str2id(pool, join(&pd, "product", ":", value), 1);
292                   continue;
293                 }
294               if (s)
295                 {
296                   /* finish old solvable */
297                   if (!s->arch)
298                     s->arch = ARCH_NOARCH;
299                   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
300                     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
301                   if (code10)
302                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
303                 }
304               /* create new solvable */
305               s = pool_id2solvable(pool, repo_add_solvable(repo));
306               repodata_extend(data, s - pool->solvables);
307               handle = s - pool->solvables;
308               s->name = str2id(pool, join(&pd, "product", ":", value), 1);
309               continue;
310             }
311
312           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
313              from here on.  */
314           if (!s)
315             {
316               s = pool_id2solvable(pool, repo_add_solvable(repo));
317               repodata_extend(data, s - pool->solvables);
318               handle = s - pool->solvables;
319             }
320
321           if (istag ("VERSION"))
322             pd.tmpvers = strdup(value);
323           else if (istag ("RELEASE"))
324             pd.tmprel = strdup(value);
325           else if (code11 && istag ("DISTRIBUTION"))
326             repo_set_str(repo, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
327           else if (istag ("DATADIR"))
328             repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, value);
329           else if (istag ("UPDATEURLS"))
330             add_multiple_urls(data, handle, value, str2id(pool, "update", 1));
331           else if (istag ("EXTRAURLS"))
332             add_multiple_urls(data, handle, value, str2id(pool, "extra", 1));
333           else if (istag ("OPTIONALURLS"))
334             add_multiple_urls(data, handle, value, str2id(pool, "optional", 1));
335           else if (istag ("RELNOTESURL"))
336             add_multiple_urls(data, handle, value, str2id(pool, "releasenotes", 1));
337           else if (istag ("SHORTLABEL"))
338             repo_set_str(repo, s - pool->solvables, PRODUCT_SHORTLABEL, value);
339           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
340             repo_set_str(repo, s - pool->solvables, SOLVABLE_SUMMARY, value);
341           else if (!strncmp (key, "LABEL.", 6))
342             repo_set_str(repo, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
343           else if (istag ("FLAGS"))
344             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
345           else if (istag ("VENDOR"))
346             s->vendor = str2id(pool, value, 1);
347           else if (istag ("BASEARCHS"))
348             {
349               char *arch;
350
351               if ((arch = splitword(&value)) != 0)
352                 {
353                   s->arch = str2id(pool, arch, 1);
354                   while ((arch = splitword(&value)) != 0)
355                     {
356                        otherarchs = sat_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
357                        otherarchs[numotherarchs++] = str2id(pool, arch, 1);
358                     }
359                 }
360             }
361
362           /*
363            * Every tag below is Code10 only
364            *
365            */
366
367           else if (code10 && istag ("CONTENTSTYLE"))
368             /* CONTENTSTYLE must be first line */
369             pool_debug(pool, SAT_ERROR, "repo_content: 'CONTENTSTYLE' must be first line of 'content'\n");
370           else if (code10 && istag ("DISTPRODUCT"))
371             /* DISTPRODUCT is for registration and Yast, not for the solver. */
372             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTPRODUCT, value);
373           else if (code10 && istag ("DISTVERSION"))
374             /* DISTVERSION is for registration and Yast, not for the solver. */
375             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTVERSION, value);
376           else if (code10 && istag ("ARCH"))
377             /* Theoretically we want to have the best arch of the given
378                modifiers which still is compatible with the system
379                arch.  We don't know the latter here, though.  */
380             s->arch = ARCH_NOARCH;
381           else if (code10 && istag ("PREREQUIRES"))
382             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
383           else if (code10 && istag ("REQUIRES"))
384             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
385           else if (code10 && istag ("PROVIDES"))
386             s->provides = adddep(pool, &pd, s->provides, value, 0);
387           else if (code10 && istag ("CONFLICTS"))
388             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
389           else if (code10 && istag ("OBSOLETES"))
390             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
391           else if (code10 && istag ("RECOMMENDS"))
392             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
393           else if (code10 && istag ("SUGGESTS"))
394             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
395           else if (code10 && istag ("SUPPLEMENTS"))
396             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
397           else if (code10 && istag ("ENHANCES"))
398             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
399           /* FRESHENS doesn't seem to exist.  */
400           else if (code10 && istag ("TYPE"))
401             repo_set_str(repo, s - pool->solvables, PRODUCT_TYPE, value);
402
403           /* XXX do something about LINGUAS and ARCH?
404           * <ma>: Don't think so. zypp does not use or propagate them.
405           */
406 #undef istag
407         }
408       else
409         pool_debug(pool, SAT_ERROR, "repo_content: malformed line: %s\n", line);
410     }
411
412   if (!s || !s->name)
413     {
414       pool_debug(pool, SAT_FATAL, "repo_content: 'content' incomplete, no product solvable created !\n");
415       exit(1);
416     }
417
418   if (pd.tmpvers)
419     {
420       if (pd.tmprel)
421         s->evr = makeevr(pool, join2(pd.tmpvers, "-", pd.tmprel));
422       else
423         s->evr = makeevr(pool, pd.tmpvers);
424     }
425   else if (pd.tmprel)
426     {
427       s->evr = makeevr(pool, join2("", "-", pd.tmprel));
428     }
429   pd.tmpvers = sat_free((void *)pd.tmpvers);
430   pd.tmprel = sat_free((void *)pd.tmprel);
431
432   if (!s->arch)
433     s->arch = ARCH_NOARCH;
434   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
435     {
436       s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
437       if (code10)
438         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
439     }
440
441   /* now for every other arch, clone the product except the architecture */
442   for (i = 0; i < numotherarchs; ++i)
443     {
444       Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
445       repodata_extend(data, p - pool->solvables);
446       p->name = s->name;
447       p->evr = s->evr;
448       p->vendor = s->vendor;
449       p->arch = otherarchs[i];
450
451       /* self provides */
452       if (p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
453           p->provides = repo_addid_dep(repo, p->provides, rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
454
455       /* now merge the attributes */
456       repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
457     }
458
459   if (pd.tmp)
460     sat_free(pd.tmp);
461   sat_free(line);
462   sat_free(otherarchs);
463   if (!(flags & REPO_NO_INTERNALIZE))
464     repodata_internalize(data);
465 }