- repo_content: store DESCRDIR
[platform/upstream/libsolv.git] / ext / 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   char *descrdir = 0;
207   char *datadir = 0;
208
209   int i = 0;
210
211   /* architectures
212      we use the first architecture in BASEARCHS or noarch
213      for the product. At the end we create (clone) the product
214      for each one of the remaining architectures
215      we allow max 4 archs
216   */
217   unsigned int numotherarchs = 0;
218   Id *otherarchs = 0;
219
220   if (!(flags & REPO_REUSE_REPODATA))
221     data = repo_add_repodata(repo, 0);
222   else
223     data = repo_last_repodata(repo);
224
225   memset(&pd, 0, sizeof(pd));
226   line = sat_malloc(1024);
227   aline = 1024;
228
229   if (repo->nrepodata)
230     /* use last repodata */
231     data = repo->repodata + repo->nrepodata - 1;
232   else
233     data = repo_add_repodata(repo, 0);
234
235   pd.repo = repo;
236   linep = line;
237   s = 0;
238
239   for (;;)
240     {
241       char *key, *value;
242
243       /* read line into big-enough buffer */
244       if (linep - line + 16 > aline)
245         {
246           aline = linep - line;
247           line = sat_realloc(line, aline + 512);
248           linep = line + aline;
249           aline += 512;
250         }
251       if (!fgets(linep, aline - (linep - line), fp))
252         break;
253       linep += strlen(linep);
254       if (linep == line || linep[-1] != '\n')
255         continue;
256       while ( --linep > line && ( linep[-1] == ' ' ||  linep[-1] == '\t' ) )
257         ; /* skip trailing ws */
258       *linep = 0;
259       linep = line;
260
261       /* expect "key value" lines */
262       value = line;
263       key = splitword(&value);
264
265       if (key)
266         {
267 #if 0
268           fprintf (stderr, "key %s, value %s\n", key, value);
269 #endif
270
271 #define istag(x) (!strcmp (key, x))
272 #define code10 (contentstyle == 10)
273 #define code11 (contentstyle == 11)
274
275           if (istag ("CONTENTSTYLE"))
276             {
277               contentstyle = atoi(value);
278               continue;
279             }
280           if (!contentstyle)
281             contentstyle = 10;
282
283           if (istag ("DESCRDIR"))
284             {
285               if (descrdir)
286                 free(descrdir);
287               else
288                 repo_set_str(repo, SOLVID_META, SUSETAGS_DESCRDIR, value);
289               if (s)
290                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DESCRDIR, value);
291               descrdir = strdup(value);
292               continue;
293             }
294           if (istag ("DATADIR"))
295             {
296               if (datadir)
297                 free(datadir);
298               else
299                 repo_set_str(repo, SOLVID_META, SUSETAGS_DATADIR, value);
300               if (s)
301                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, value);
302               datadir = strdup(value);
303               continue;
304             }
305
306           /* XXX: add those to SOLVID_META, too */
307           if (istag ("META"))
308             continue;
309           if (istag ("HASH"))
310             continue;
311           if (istag ("KEY"))
312             continue;
313
314           if ((code10 && istag ("PRODUCT"))
315               || (code11 && istag ("NAME")))
316             {
317               if (s && !s->name)
318                 {
319                   /* this solvable was created without seeing a
320                      PRODUCT entry, just set the name and continue */
321                   s->name = str2id(pool, join(&pd, "product", ":", value), 1);
322                   continue;
323                 }
324               if (s)
325                 {
326                   /* finish old solvable */
327                   if (!s->arch)
328                     s->arch = ARCH_NOARCH;
329                   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
330                     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
331                   if (code10)
332                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
333                 }
334               /* create new solvable */
335               s = pool_id2solvable(pool, repo_add_solvable(repo));
336               repodata_extend(data, s - pool->solvables);
337               handle = s - pool->solvables;
338               s->name = str2id(pool, join(&pd, "product", ":", value), 1);
339               if (datadir)
340                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, datadir);
341               if (descrdir)
342                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
343               continue;
344             }
345
346           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
347              from here on.  */
348           if (!s)
349             {
350               s = pool_id2solvable(pool, repo_add_solvable(repo));
351               repodata_extend(data, s - pool->solvables);
352               handle = s - pool->solvables;
353             }
354
355           if (istag ("VERSION"))
356             pd.tmpvers = strdup(value);
357           else if (istag ("RELEASE"))
358             pd.tmprel = strdup(value);
359           else if (code11 && istag ("DISTRIBUTION"))
360             repo_set_str(repo, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
361           else if (istag ("UPDATEURLS"))
362             add_multiple_urls(data, handle, value, str2id(pool, "update", 1));
363           else if (istag ("EXTRAURLS"))
364             add_multiple_urls(data, handle, value, str2id(pool, "extra", 1));
365           else if (istag ("OPTIONALURLS"))
366             add_multiple_urls(data, handle, value, str2id(pool, "optional", 1));
367           else if (istag ("RELNOTESURL"))
368             add_multiple_urls(data, handle, value, str2id(pool, "releasenotes", 1));
369           else if (istag ("SHORTLABEL"))
370             repo_set_str(repo, s - pool->solvables, PRODUCT_SHORTLABEL, value);
371           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
372             repo_set_str(repo, s - pool->solvables, SOLVABLE_SUMMARY, value);
373           else if (!strncmp (key, "LABEL.", 6))
374             repo_set_str(repo, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
375           else if (istag ("FLAGS"))
376             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
377           else if (istag ("VENDOR"))
378             s->vendor = str2id(pool, value, 1);
379           else if (istag ("BASEARCHS"))
380             {
381               char *arch;
382
383               if ((arch = splitword(&value)) != 0)
384                 {
385                   s->arch = str2id(pool, arch, 1);
386                   while ((arch = splitword(&value)) != 0)
387                     {
388                        otherarchs = sat_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
389                        otherarchs[numotherarchs++] = str2id(pool, arch, 1);
390                     }
391                 }
392             }
393
394           /*
395            * Every tag below is Code10 only
396            *
397            */
398
399           else if (code10 && istag ("CONTENTSTYLE"))
400             /* CONTENTSTYLE must be first line */
401             pool_debug(pool, SAT_ERROR, "repo_content: 'CONTENTSTYLE' must be first line of 'content'\n");
402           else if (code10 && istag ("DISTPRODUCT"))
403             /* DISTPRODUCT is for registration and Yast, not for the solver. */
404             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTPRODUCT, value);
405           else if (code10 && istag ("DISTVERSION"))
406             /* DISTVERSION is for registration and Yast, not for the solver. */
407             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTVERSION, value);
408           else if (code10 && istag ("ARCH"))
409             /* Theoretically we want to have the best arch of the given
410                modifiers which still is compatible with the system
411                arch.  We don't know the latter here, though.  */
412             s->arch = ARCH_NOARCH;
413           else if (code10 && istag ("PREREQUIRES"))
414             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
415           else if (code10 && istag ("REQUIRES"))
416             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
417           else if (code10 && istag ("PROVIDES"))
418             s->provides = adddep(pool, &pd, s->provides, value, 0);
419           else if (code10 && istag ("CONFLICTS"))
420             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
421           else if (code10 && istag ("OBSOLETES"))
422             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
423           else if (code10 && istag ("RECOMMENDS"))
424             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
425           else if (code10 && istag ("SUGGESTS"))
426             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
427           else if (code10 && istag ("SUPPLEMENTS"))
428             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
429           else if (code10 && istag ("ENHANCES"))
430             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
431           /* FRESHENS doesn't seem to exist.  */
432           else if (code10 && istag ("TYPE"))
433             repo_set_str(repo, s - pool->solvables, PRODUCT_TYPE, value);
434
435           /* XXX do something about LINGUAS and ARCH?
436           * <ma>: Don't think so. zypp does not use or propagate them.
437           */
438 #undef istag
439         }
440       else
441         pool_debug(pool, SAT_ERROR, "repo_content: malformed line: %s\n", line);
442     }
443
444   if (datadir)
445     free(datadir);
446   if (descrdir)
447     free(descrdir);
448
449   if (!s || !s->name)
450     {
451       pool_debug(pool, SAT_FATAL, "repo_content: 'content' incomplete, no product solvable created!\n");
452       exit(1);
453     }
454
455   if (pd.tmpvers)
456     {
457       if (pd.tmprel)
458         s->evr = makeevr(pool, join2(pd.tmpvers, "-", pd.tmprel));
459       else
460         s->evr = makeevr(pool, pd.tmpvers);
461     }
462   else if (pd.tmprel)
463     {
464       s->evr = makeevr(pool, join2("", "-", pd.tmprel));
465     }
466   pd.tmpvers = sat_free((void *)pd.tmpvers);
467   pd.tmprel = sat_free((void *)pd.tmprel);
468
469   if (!s->arch)
470     s->arch = ARCH_NOARCH;
471   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
472     {
473       s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
474       if (code10)
475         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
476     }
477
478   /* now for every other arch, clone the product except the architecture */
479   for (i = 0; i < numotherarchs; ++i)
480     {
481       Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
482       repodata_extend(data, p - pool->solvables);
483       p->name = s->name;
484       p->evr = s->evr;
485       p->vendor = s->vendor;
486       p->arch = otherarchs[i];
487
488       /* self provides */
489       if (p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
490           p->provides = repo_addid_dep(repo, p->provides, rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
491
492       /* now merge the attributes */
493       repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
494     }
495
496   if (pd.tmp)
497     sat_free(pd.tmp);
498   sat_free(line);
499   sat_free(otherarchs);
500   join_freemem();
501   if (!(flags & REPO_NO_INTERNALIZE))
502     repodata_internalize(data);
503 }