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