- add repodata_join function to join a solvable block
[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 ("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           if (istag ("VENDOR"))
306             {
307               if (defvendor)
308                 free(defvendor);
309               else
310                 repo_set_poolstr(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
311               if (s)
312                 s->vendor = str2id(pool, value, 1);
313               defvendor = strdup(value);
314               continue;
315             }
316
317           if (istag ("META") || istag ("HASH") || istag ("KEY"))
318             {
319               char *checksumtype, *checksum;
320               Id fh, type;
321               int l;
322
323               if ((checksumtype = splitword(&value)) == 0)
324                 continue;
325               if ((checksum = splitword(&value)) == 0)
326                 continue;
327               if (!*value)
328                 continue;
329               if (!strcasecmp(checksumtype, "sha") || !strcasecmp(checksumtype, "sha1"))
330                 l = SIZEOF_SHA1 * 2, type = REPOKEY_TYPE_SHA1;
331               else if (!strcasecmp(checksumtype, "sha256"))
332                 l = SIZEOF_SHA256 * 2, type = REPOKEY_TYPE_SHA256;
333               else if (!strcasecmp(checksumtype, "md5"))
334                 l = SIZEOF_MD5 * 2, type = REPOKEY_TYPE_MD5;
335               else
336                 {
337                   fprintf(stderr, "Unknown checksum type: %s: %s\n", value, checksumtype);
338                   exit(1);
339                 }
340               if (strlen(checksum) != l)
341                 {
342                   fprintf(stderr, "Invalid checksum length: %s: for %s\n", value, checksum);
343                   exit(1);
344                 }
345               fh = repodata_new_handle(data);
346               repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
347               repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
348               repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
349               repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
350               continue;
351             }
352
353           /* product tags */
354
355           if ((code10 && istag ("PRODUCT"))
356               || (code11 && istag ("NAME")))
357             {
358               if (s && !s->name)
359                 {
360                   /* this solvable was created without seeing a
361                      PRODUCT entry, just set the name and continue */
362                   s->name = str2id(pool, join(&pd, "product", ":", value), 1);
363                   continue;
364                 }
365               if (s)
366                 {
367                   /* finish old solvable */
368                   if (!s->arch)
369                     s->arch = ARCH_NOARCH;
370                   if (!s->evr)
371                     s->evr = ID_EMPTY;
372                   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
373                     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
374                   if (code10)
375                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
376                 }
377               /* create new solvable */
378               s = pool_id2solvable(pool, repo_add_solvable(repo));
379               repodata_extend(data, s - pool->solvables);
380               handle = s - pool->solvables;
381               s->name = str2id(pool, join(&pd, "product", ":", value), 1);
382               if (datadir)
383                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, datadir);
384               if (descrdir)
385                 repo_set_str(repo, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
386               if (defvendor)
387                 s->vendor = str2id(pool, defvendor, 1);
388               continue;
389             }
390
391           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
392              from here on.  */
393           if (!s)
394             {
395               s = pool_id2solvable(pool, repo_add_solvable(repo));
396               repodata_extend(data, s - pool->solvables);
397               handle = s - pool->solvables;
398             }
399
400           if (istag ("VERSION"))
401             pd.tmpvers = strdup(value);
402           else if (istag ("RELEASE"))
403             pd.tmprel = strdup(value);
404           else if (code11 && istag ("DISTRIBUTION"))
405             repo_set_str(repo, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
406           else if (istag ("UPDATEURLS"))
407             add_multiple_urls(data, handle, value, str2id(pool, "update", 1));
408           else if (istag ("EXTRAURLS"))
409             add_multiple_urls(data, handle, value, str2id(pool, "extra", 1));
410           else if (istag ("OPTIONALURLS"))
411             add_multiple_urls(data, handle, value, str2id(pool, "optional", 1));
412           else if (istag ("RELNOTESURL"))
413             add_multiple_urls(data, handle, value, str2id(pool, "releasenotes", 1));
414           else if (istag ("SHORTLABEL"))
415             repo_set_str(repo, s - pool->solvables, PRODUCT_SHORTLABEL, value);
416           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
417             repo_set_str(repo, s - pool->solvables, SOLVABLE_SUMMARY, value);
418           else if (!strncmp (key, "LABEL.", 6))
419             repo_set_str(repo, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
420           else if (istag ("FLAGS"))
421             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
422           else if (istag ("VENDOR"))    /* actually already handled above */
423             s->vendor = str2id(pool, value, 1);
424           else if (istag ("BASEARCHS"))
425             {
426               char *arch;
427
428               if ((arch = splitword(&value)) != 0)
429                 {
430                   s->arch = str2id(pool, arch, 1);
431                   while ((arch = splitword(&value)) != 0)
432                     {
433                        otherarchs = sat_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
434                        otherarchs[numotherarchs++] = str2id(pool, arch, 1);
435                     }
436                 }
437             }
438
439           /*
440            * Every tag below is Code10 only
441            *
442            */
443
444           if (code10 && istag ("DISTPRODUCT"))
445             /* DISTPRODUCT is for registration and Yast, not for the solver. */
446             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTPRODUCT, value);
447           else if (code10 && istag ("DISTVERSION"))
448             /* DISTVERSION is for registration and Yast, not for the solver. */
449             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTVERSION, value);
450           else if (code10 && istag ("ARCH"))
451             /* Theoretically we want to have the best arch of the given
452                modifiers which still is compatible with the system
453                arch.  We don't know the latter here, though.  */
454             s->arch = ARCH_NOARCH;
455           else if (code10 && istag ("PREREQUIRES"))
456             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
457           else if (code10 && istag ("REQUIRES"))
458             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
459           else if (code10 && istag ("PROVIDES"))
460             s->provides = adddep(pool, &pd, s->provides, value, 0);
461           else if (code10 && istag ("CONFLICTS"))
462             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
463           else if (code10 && istag ("OBSOLETES"))
464             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
465           else if (code10 && istag ("RECOMMENDS"))
466             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
467           else if (code10 && istag ("SUGGESTS"))
468             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
469           else if (code10 && istag ("SUPPLEMENTS"))
470             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
471           else if (code10 && istag ("ENHANCES"))
472             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
473           /* FRESHENS doesn't seem to exist.  */
474           else if (code10 && istag ("TYPE"))
475             repo_set_str(repo, s - pool->solvables, PRODUCT_TYPE, value);
476
477           /* XXX do something about LINGUAS and ARCH?
478           * <ma>: Don't think so. zypp does not use or propagate them.
479           */
480 #undef istag
481         }
482       else
483         pool_debug(pool, SAT_ERROR, "repo_content: malformed line: %s\n", line);
484     }
485
486   if (datadir)
487     free(datadir);
488   if (descrdir)
489     free(descrdir);
490   if (defvendor)
491     free(defvendor);
492
493   if (s)
494     {
495       if (!s->name)
496         {
497           pool_debug(pool, SAT_FATAL, "repo_content: 'content' incomplete, no product solvable created!\n");
498           exit(1);
499         }
500
501       if (pd.tmprel)
502         s->evr = makeevr(pool, join(&pd, pd.tmpvers, "-", pd.tmprel));
503       else
504         s->evr = makeevr(pool, pd.tmpvers);
505       pd.tmpvers = sat_free((void *)pd.tmpvers);
506       pd.tmprel = sat_free((void *)pd.tmprel);
507
508       if (!s->arch)
509         s->arch = ARCH_NOARCH;
510       if (!s->evr)
511         s->evr = ID_EMPTY;
512       if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
513         s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
514       if (code10)
515         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
516
517       /* now for every other arch, clone the product except the architecture */
518       for (i = 0; i < numotherarchs; ++i)
519         {
520           Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
521           repodata_extend(data, p - pool->solvables);
522           p->name = s->name;
523           p->evr = s->evr;
524           p->vendor = s->vendor;
525           p->arch = otherarchs[i];
526
527           /* self provides */
528           if (s->name && p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
529               p->provides = repo_addid_dep(repo, p->provides, rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
530
531           /* now merge the attributes */
532           repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
533         }
534     }
535
536   if (pd.tmp)
537     sat_free(pd.tmp);
538   sat_free(line);
539   sat_free(otherarchs);
540   join_freemem();
541   if (!(flags & REPO_NO_INTERNALIZE))
542     repodata_internalize(data);
543 }