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