Merge branch 'master' of gitorious.org:opensuse/sat-solver
[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   *p = 0;
111   return pd->tmp;
112 }
113
114
115 /*
116  * add dependency to pool
117  * OBSOLETES product:SUSE_LINUX product:openSUSE < 11.0 package:openSUSE < 11.0
118  */
119
120 static unsigned int
121 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker)
122 {
123   char *name;
124   Id id;
125
126   while ((name = splitword(&line)) != 0)
127     {
128       /* Hack, as the content file adds 'package:' for package
129          dependencies sometimes.  */
130       if (!strncmp (name, "package:", 8))
131         name += 8;
132       id = str2id(pool, name, 1);
133       if (*line == '<' || *line == '>' || *line == '=') /* rel follows */
134         {
135           char *rel = splitword(&line);
136           char *evr = splitword(&line);
137           int flags;
138
139           if (!rel || !evr)
140             {
141               pool_debug(pool, SAT_FATAL, "repo_content: bad relation '%s %s'\n", name, rel);
142               exit(1);
143             }
144           for (flags = 0; flags < 6; flags++)
145             if (!strcmp(rel, flagtab[flags]))
146               break;
147           if (flags == 6)
148             {
149               pool_debug(pool, SAT_FATAL, "repo_content: unknown relation '%s'\n", rel);
150               exit(1);
151             }
152           id = rel2id(pool, id, str2id(pool, evr, 1), flags + 1, 1);
153         }
154       olddeps = repo_addid_dep(pd->repo, olddeps, id, marker);
155     }
156   return olddeps;
157 }
158
159
160 /*
161  * split value and add to pool
162  */
163
164 static void
165 add_multiple_strings(Repodata *data, Id handle, Id name, char *value)
166 {
167   char *str;
168
169   while ((str = splitword(&value)) != 0)
170     repodata_add_poolstr_array(data, handle, name, str);
171 }
172
173 /*
174  * split value and add to pool
175  */
176
177 static void
178 add_multiple_urls(Repodata *data, Id handle, char *value, Id type)
179 {
180   char *url;
181
182   while ((url = splitword(&value)) != 0)
183     {
184       repodata_add_poolstr_array(data, handle, PRODUCT_URL, url);
185       repodata_add_idarray(data, handle, PRODUCT_URL_TYPE, type);
186     }
187 }
188
189
190
191 /*
192  * add 'content' to repo
193  *
194  */
195
196 void
197 repo_add_content(Repo *repo, FILE *fp, int flags)
198 {
199   Pool *pool = repo->pool;
200   char *line, *linep;
201   int aline;
202   Solvable *s;
203   struct parsedata pd;
204   Repodata *data;
205   Id handle = 0;
206   int contentstyle = 0;
207   char *descrdir = 0;
208   char *datadir = 0;
209   char *defvendor = 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   pd.repo = repo;
227   linep = line;
228   s = 0;
229
230   data = repo_add_repodata(repo, flags);
231
232   for (;;)
233     {
234       char *key, *value;
235
236       /* read line into big-enough buffer */
237       if (linep - line + 16 > aline)
238         {
239           aline = linep - line;
240           line = sat_realloc(line, aline + 512);
241           linep = line + aline;
242           aline += 512;
243         }
244       if (!fgets(linep, aline - (linep - line), fp))
245         break;
246       linep += strlen(linep);
247       if (linep == line || linep[-1] != '\n')
248         continue;
249       while ( --linep > line && ( linep[-1] == ' ' ||  linep[-1] == '\t' ) )
250         ; /* skip trailing ws */
251       *linep = 0;
252       linep = line;
253
254       /* expect "key value" lines */
255       value = line;
256       key = splitword(&value);
257
258       if (key)
259         {
260 #if 0
261           fprintf (stderr, "key %s, value %s\n", key, value);
262 #endif
263
264 #define istag(x) (!strcmp (key, x))
265 #define code10 (contentstyle == 10)
266 #define code11 (contentstyle == 11)
267
268
269           if (istag ("CONTENTSTYLE"))
270             {
271               if (contentstyle)
272                 pool_debug(pool, SAT_ERROR, "repo_content: 'CONTENTSTYLE' must be first line of 'content'\n");
273               contentstyle = atoi(value);
274               continue;
275             }
276           if (!contentstyle)
277             contentstyle = 10;
278
279           /* repository tags */
280           /* we also replicate some of them into the product solvables
281            * to be backward compatible */
282
283           if (istag ("REPOID"))
284             {
285               repodata_add_poolstr_array(data, SOLVID_META, REPOSITORY_REPOID, value);
286               continue;
287             }
288
289           if (istag ("DESCRDIR"))
290             {
291               if (descrdir)
292                 free(descrdir);
293               else
294                 repo_set_str(repo, SOLVID_META, SUSETAGS_DESCRDIR, value);
295               if (s)
296                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DESCRDIR, value);
297               descrdir = strdup(value);
298               continue;
299             }
300           if (istag ("DATADIR"))
301             {
302               if (datadir)
303                 free(datadir);
304               else
305                 repo_set_str(repo, SOLVID_META, SUSETAGS_DATADIR, value);
306               if (s)
307                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, value);
308               datadir = strdup(value);
309               continue;
310             }
311           if (istag ("VENDOR"))
312             {
313               if (defvendor)
314                 free(defvendor);
315               else
316                 repo_set_poolstr(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
317               if (s)
318                 s->vendor = str2id(pool, value, 1);
319               defvendor = strdup(value);
320               continue;
321             }
322
323           if (istag ("META") || istag ("HASH") || istag ("KEY"))
324             {
325               char *checksumtype, *checksum;
326               Id fh, type;
327               int l;
328
329               if ((checksumtype = splitword(&value)) == 0)
330                 continue;
331               if ((checksum = splitword(&value)) == 0)
332                 continue;
333               if (!*value)
334                 continue;
335               if (!strcasecmp(checksumtype, "sha") || !strcasecmp(checksumtype, "sha1"))
336                 l = SIZEOF_SHA1 * 2, type = REPOKEY_TYPE_SHA1;
337               else if (!strcasecmp(checksumtype, "sha256"))
338                 l = SIZEOF_SHA256 * 2, type = REPOKEY_TYPE_SHA256;
339               else if (!strcasecmp(checksumtype, "md5"))
340                 l = SIZEOF_MD5 * 2, type = REPOKEY_TYPE_MD5;
341               else
342                 {
343                   fprintf(stderr, "Unknown checksum type: %s: %s\n", value, checksumtype);
344                   exit(1);
345                 }
346               if (strlen(checksum) != l)
347                 {
348                   fprintf(stderr, "Invalid checksum length: %s: for %s\n", value, checksum);
349                   exit(1);
350                 }
351               fh = repodata_new_handle(data);
352               repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
353               repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
354               repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
355               repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
356               continue;
357             }
358
359           /* product tags */
360
361           if ((code10 && istag ("PRODUCT"))
362               || (code11 && istag ("NAME")))
363             {
364               if (s && !s->name)
365                 {
366                   /* this solvable was created without seeing a
367                      PRODUCT entry, just set the name and continue */
368                   s->name = str2id(pool, join(&pd, "product", ":", value), 1);
369                   continue;
370                 }
371               if (s)
372                 {
373                   /* finish old solvable */
374                   if (!s->arch)
375                     s->arch = ARCH_NOARCH;
376                   if (!s->evr)
377                     s->evr = ID_EMPTY;
378                   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
379                     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
380                   if (code10)
381                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
382                 }
383               /* create new solvable */
384               s = pool_id2solvable(pool, repo_add_solvable(repo));
385               repodata_extend(data, s - pool->solvables);
386               handle = s - pool->solvables;
387               s->name = str2id(pool, join(&pd, "product", ":", value), 1);
388               if (datadir)
389                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, datadir);
390               if (descrdir)
391                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
392               if (defvendor)
393                 s->vendor = str2id(pool, defvendor, 1);
394               continue;
395             }
396
397           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
398              from here on.  */
399           if (!s)
400             {
401               s = pool_id2solvable(pool, repo_add_solvable(repo));
402               repodata_extend(data, s - pool->solvables);
403               handle = s - pool->solvables;
404             }
405
406           if (istag ("VERSION"))
407             pd.tmpvers = strdup(value);
408           else if (istag ("RELEASE"))
409             pd.tmprel = strdup(value);
410           else if (code11 && istag ("DISTRIBUTION"))
411             repo_set_str(repo, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
412           else if (istag ("UPDATEURLS"))
413             add_multiple_urls(data, handle, value, str2id(pool, "update", 1));
414           else if (istag ("EXTRAURLS"))
415             add_multiple_urls(data, handle, value, str2id(pool, "extra", 1));
416           else if (istag ("OPTIONALURLS"))
417             add_multiple_urls(data, handle, value, str2id(pool, "optional", 1));
418           else if (istag ("RELNOTESURL"))
419             add_multiple_urls(data, handle, value, str2id(pool, "releasenotes", 1));
420           else if (istag ("SHORTLABEL"))
421             repo_set_str(repo, s - pool->solvables, PRODUCT_SHORTLABEL, value);
422           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
423             repo_set_str(repo, s - pool->solvables, SOLVABLE_SUMMARY, value);
424           else if (!strncmp (key, "LABEL.", 6))
425             repo_set_str(repo, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
426           else if (istag ("FLAGS"))
427             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
428           else if (istag ("VENDOR"))    /* actually already handled above */
429             s->vendor = str2id(pool, value, 1);
430           else if (istag ("BASEARCHS"))
431             {
432               char *arch;
433
434               if ((arch = splitword(&value)) != 0)
435                 {
436                   s->arch = str2id(pool, arch, 1);
437                   while ((arch = splitword(&value)) != 0)
438                     {
439                        otherarchs = sat_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
440                        otherarchs[numotherarchs++] = str2id(pool, arch, 1);
441                     }
442                 }
443             }
444
445           /*
446            * Every tag below is Code10 only
447            *
448            */
449
450           if (code10 && istag ("DISTPRODUCT"))
451             /* DISTPRODUCT is for registration and Yast, not for the solver. */
452             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTPRODUCT, value);
453           else if (code10 && istag ("DISTVERSION"))
454             /* DISTVERSION is for registration and Yast, not for the solver. */
455             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTVERSION, value);
456           else if (code10 && istag ("ARCH"))
457             /* Theoretically we want to have the best arch of the given
458                modifiers which still is compatible with the system
459                arch.  We don't know the latter here, though.  */
460             s->arch = ARCH_NOARCH;
461           else if (code10 && istag ("PREREQUIRES"))
462             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
463           else if (code10 && istag ("REQUIRES"))
464             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
465           else if (code10 && istag ("PROVIDES"))
466             s->provides = adddep(pool, &pd, s->provides, value, 0);
467           else if (code10 && istag ("CONFLICTS"))
468             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
469           else if (code10 && istag ("OBSOLETES"))
470             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
471           else if (code10 && istag ("RECOMMENDS"))
472             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
473           else if (code10 && istag ("SUGGESTS"))
474             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
475           else if (code10 && istag ("SUPPLEMENTS"))
476             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
477           else if (code10 && istag ("ENHANCES"))
478             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
479           /* FRESHENS doesn't seem to exist.  */
480           else if (code10 && istag ("TYPE"))
481             repo_set_str(repo, s - pool->solvables, PRODUCT_TYPE, value);
482
483           /* XXX do something about LINGUAS and ARCH?
484           * <ma>: Don't think so. zypp does not use or propagate them.
485           */
486 #undef istag
487         }
488       else
489         pool_debug(pool, SAT_ERROR, "repo_content: malformed line: %s\n", line);
490     }
491
492   if (datadir)
493     free(datadir);
494   if (descrdir)
495     free(descrdir);
496   if (defvendor)
497     free(defvendor);
498
499   if (s)
500     {
501       if (!s->name)
502         {
503           pool_debug(pool, SAT_FATAL, "repo_content: 'content' incomplete, no product solvable created!\n");
504           exit(1);
505         }
506
507       if (pd.tmprel)
508         s->evr = makeevr(pool, join(&pd, pd.tmpvers, "-", pd.tmprel));
509       else
510         s->evr = makeevr(pool, pd.tmpvers);
511       pd.tmpvers = sat_free((void *)pd.tmpvers);
512       pd.tmprel = sat_free((void *)pd.tmprel);
513
514       if (!s->arch)
515         s->arch = ARCH_NOARCH;
516       if (!s->evr)
517         s->evr = ID_EMPTY;
518       if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
519         s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
520       if (code10)
521         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
522
523       /* now for every other arch, clone the product except the architecture */
524       for (i = 0; i < numotherarchs; ++i)
525         {
526           Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
527           repodata_extend(data, p - pool->solvables);
528           p->name = s->name;
529           p->evr = s->evr;
530           p->vendor = s->vendor;
531           p->arch = otherarchs[i];
532
533           /* self provides */
534           if (s->name && p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
535               p->provides = repo_addid_dep(repo, p->provides, rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
536
537           /* now merge the attributes */
538           repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
539         }
540     }
541
542   if (pd.tmp)
543     sat_free(pd.tmp);
544   sat_free(line);
545   sat_free(otherarchs);
546   join_freemem();
547   if (!(flags & REPO_NO_INTERNALIZE))
548     repodata_internalize(data);
549 }