make repodata_stringify return the result string
[platform/upstream/libsolv.git] / ext / repo_content.c
1 /*
2  * repo_content.c
3  *
4  * Parses 'content' file into .solv
5  * A 'content' file is the repomd.xml of the susetags format
6  *
7  * See http://en.opensuse.org/Standards/YaST2_Repository_Metadata/content for a description
8  * of the syntax
9  *
10  *
11  * Copyright (c) 2007, Novell Inc.
12  *
13  * This program is licensed under the BSD license, read LICENSE.BSD
14  * for further information
15  */
16
17 #include <sys/types.h>
18 #include <limits.h>
19 #include <fcntl.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24
25 #include "pool.h"
26 #include "repo.h"
27 #include "util.h"
28 #include "chksum.h"
29 #include "repo_content.h"
30 #define DISABLE_SPLIT
31 #define DISABLE_JOIN2
32 #include "tools_util.h"
33
34 /* split off a word, return null terminated pointer to it.
35  * return NULL if there is no word left. */
36 static char *
37 splitword(char **lp)
38 {
39   char *w, *l = *lp;
40
41   while (*l == ' ' || *l == '\t')
42     l++;
43   w = *l ? l : 0;
44   while (*l && *l != ' ' && *l != '\t')
45     l++;
46   if (*l)
47     *l++ = 0;           /* terminate word */
48   while (*l == ' ' || *l == '\t')
49     l++;                /* convenience: advance to next word */
50   *lp = l;
51   return w;
52 }
53
54 struct parsedata {
55   Repo *repo;
56   char *tmp;
57   int tmpl;
58
59   const char *tmpvers;
60   const char *tmprel;
61 };
62
63 /*
64  * dependency relations
65  */
66
67 static char *flagtab[] = {
68   ">",
69   "=",
70   ">=",
71   "<",
72   "!=",
73   "<="
74 };
75
76
77 /*
78  * join up to three strings into one
79  */
80
81 static char *
82 join(struct parsedata *pd, const char *s1, const char *s2, const char *s3)
83 {
84   int l = 1;
85   char *p;
86
87   if (s1)
88     l += strlen(s1);
89   if (s2)
90     l += strlen(s2);
91   if (s3)
92     l += strlen(s3);
93   if (l > pd->tmpl)
94     {
95       pd->tmpl = l + 256;
96       pd->tmp = solv_realloc(pd->tmp, pd->tmpl);
97     }
98   p = pd->tmp;
99   if (s1)
100     {
101       strcpy(p, s1);
102       p += strlen(s1);
103     }
104   if (s2)
105     {
106       strcpy(p, s2);
107       p += strlen(s2);
108     }
109   if (s3)
110     {
111       strcpy(p, s3);
112       p += strlen(s3);
113     }
114   *p = 0;
115   return pd->tmp;
116 }
117
118
119 /*
120  * add dependency to pool
121  * OBSOLETES product:SUSE_LINUX product:openSUSE < 11.0 package:openSUSE < 11.0
122  */
123
124 static unsigned int
125 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker)
126 {
127   char *name;
128   Id id;
129
130   while ((name = splitword(&line)) != 0)
131     {
132       /* Hack, as the content file adds 'package:' for package
133          dependencies sometimes.  */
134       if (!strncmp (name, "package:", 8))
135         name += 8;
136       id = pool_str2id(pool, name, 1);
137       if (*line == '<' || *line == '>' || *line == '=') /* rel follows */
138         {
139           char *rel = splitword(&line);
140           char *evr = splitword(&line);
141           int flags;
142
143           if (!rel || !evr)
144             {
145               pool_debug(pool, SOLV_FATAL, "repo_content: bad relation '%s %s'\n", name, rel);
146               continue;
147             }
148           for (flags = 0; flags < 6; flags++)
149             if (!strcmp(rel, flagtab[flags]))
150               break;
151           if (flags == 6)
152             {
153               pool_debug(pool, SOLV_FATAL, "repo_content: unknown relation '%s'\n", rel);
154               continue;
155             }
156           id = pool_rel2id(pool, id, pool_str2id(pool, evr, 1), flags + 1, 1);
157         }
158       olddeps = repo_addid_dep(pd->repo, olddeps, id, marker);
159     }
160   return olddeps;
161 }
162
163
164 /*
165  * split value and add to pool
166  */
167
168 static void
169 add_multiple_strings(Repodata *data, Id handle, Id name, char *value)
170 {
171   char *str;
172
173   while ((str = splitword(&value)) != 0)
174     repodata_add_poolstr_array(data, handle, name, str);
175 }
176
177 /*
178  * split value and add to pool
179  */
180
181 static void
182 add_multiple_urls(Repodata *data, Id handle, char *value, Id type)
183 {
184   char *url;
185
186   while ((url = splitword(&value)) != 0)
187     {
188       repodata_add_poolstr_array(data, handle, PRODUCT_URL, url);
189       repodata_add_idarray(data, handle, PRODUCT_URL_TYPE, type);
190     }
191 }
192
193
194
195 /*
196  * add 'content' to repo
197  *
198  */
199
200 int
201 repo_add_content(Repo *repo, FILE *fp, int flags)
202 {
203   Pool *pool = repo->pool;
204   char *line, *linep;
205   int aline;
206   Solvable *s;
207   struct parsedata pd;
208   Repodata *data;
209   Id handle = 0;
210   int contentstyle = 0;
211   char *descrdir = 0;
212   char *datadir = 0;
213   char *defvendor = 0;
214
215   int i = 0;
216   int res = 0;
217
218   /* architectures
219      we use the first architecture in BASEARCHS or noarch
220      for the product. At the end we create (clone) the product
221      for each one of the remaining architectures
222      we allow max 4 archs
223   */
224   unsigned int numotherarchs = 0;
225   Id *otherarchs = 0;
226
227   memset(&pd, 0, sizeof(pd));
228   line = solv_malloc(1024);
229   aline = 1024;
230
231   pd.repo = repo;
232   linep = line;
233   s = 0;
234
235   data = repo_add_repodata(repo, flags);
236
237   for (;;)
238     {
239       char *key, *value;
240
241       /* read line into big-enough buffer */
242       if (linep - line + 16 > aline)
243         {
244           aline = linep - line;
245           line = solv_realloc(line, aline + 512);
246           linep = line + aline;
247           aline += 512;
248         }
249       if (!fgets(linep, aline - (linep - line), fp))
250         break;
251       linep += strlen(linep);
252       if (linep == line || linep[-1] != '\n')
253         continue;
254       while ( --linep > line && ( linep[-1] == ' ' ||  linep[-1] == '\t' ) )
255         ; /* skip trailing ws */
256       *linep = 0;
257       linep = line;
258
259       /* expect "key value" lines */
260       value = line;
261       key = splitword(&value);
262
263       if (key)
264         {
265 #if 0
266           fprintf (stderr, "key %s, value %s\n", key, value);
267 #endif
268
269 #define istag(x) (!strcmp (key, x))
270 #define code10 (contentstyle == 10)
271 #define code11 (contentstyle == 11)
272
273
274           if (istag ("CONTENTSTYLE"))
275             {
276               if (contentstyle)
277                 pool_debug(pool, SOLV_ERROR, "repo_content: 'CONTENTSTYLE' must be first line of 'content'\n");
278               contentstyle = atoi(value);
279               continue;
280             }
281           if (!contentstyle)
282             contentstyle = 10;
283
284           /* repository tags */
285           /* we also replicate some of them into the product solvables
286            * to be backward compatible */
287
288           if (istag ("REPOID"))
289             {
290               repodata_add_poolstr_array(data, SOLVID_META, REPOSITORY_REPOID, value);
291               continue;
292             }
293           if (istag ("REPOKEYWORDS"))
294             {
295               add_multiple_strings(data, SOLVID_META, REPOSITORY_KEYWORDS, value);
296               continue;
297             }
298           if (istag ("DISTRO"))
299             {
300               Id dh = repodata_new_handle(data);
301               char *p;
302               /* like with createrepo --distro */
303               if ((p = strchr(value, ',')) != 0)
304                 {
305                   *p++ = 0;
306                   if (*value)
307                     repodata_set_poolstr(data, dh, REPOSITORY_PRODUCT_CPEID, value);
308                 }
309               else
310                 p = value;
311               if (*p)
312                 repodata_set_str(data, dh, REPOSITORY_PRODUCT_LABEL, p);
313               repodata_add_flexarray(data, SOLVID_META, REPOSITORY_DISTROS, dh);
314               continue;
315             }
316
317           if (istag ("DESCRDIR"))
318             {
319               if (descrdir)
320                 free(descrdir);
321               else
322                 repodata_set_str(data, SOLVID_META, SUSETAGS_DESCRDIR, value);
323               if (s)
324                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, value);
325               descrdir = solv_strdup(value);
326               continue;
327             }
328           if (istag ("DATADIR"))
329             {
330               if (datadir)
331                 free(datadir);
332               else
333                 repodata_set_str(data, SOLVID_META, SUSETAGS_DATADIR, value);
334               if (s)
335                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, value);
336               datadir = solv_strdup(value);
337               continue;
338             }
339           if (istag ("VENDOR"))
340             {
341               if (defvendor)
342                 free(defvendor);
343               else
344                 repodata_set_poolstr(data, SOLVID_META, SUSETAGS_DEFAULTVENDOR, value);
345               if (s)
346                 s->vendor = pool_str2id(pool, value, 1);
347               defvendor = solv_strdup(value);
348               continue;
349             }
350
351           if (istag ("META") || istag ("HASH") || istag ("KEY"))
352             {
353               char *checksumtype, *checksum;
354               Id fh, type;
355               int l;
356
357               if ((checksumtype = splitword(&value)) == 0)
358                 continue;
359               if ((checksum = splitword(&value)) == 0)
360                 continue;
361               if (!*value)
362                 continue;
363               type = solv_chksum_str2type(checksumtype);
364               if (!type)
365                 {
366                   pool_error(pool, -1, "%s: unknown checksum type '%s'", value, checksumtype);
367                   res = 1;
368                   continue;
369                 }
370               l = solv_chksum_len(type);
371               if (strlen(checksum) != 2 * l)
372                 {
373                   pool_error(pool, -1, "%s: invalid checksum length for %s", value, checksumtype);
374                   res = 1;
375                   continue;
376                 }
377               fh = repodata_new_handle(data);
378               repodata_set_poolstr(data, fh, SUSETAGS_FILE_TYPE, key);
379               repodata_set_str(data, fh, SUSETAGS_FILE_NAME, value);
380               repodata_set_checksum(data, fh, SUSETAGS_FILE_CHECKSUM, type, checksum);
381               repodata_add_flexarray(data, SOLVID_META, SUSETAGS_FILE, fh);
382               continue;
383             }
384
385           /* product tags */
386
387           if ((code10 && istag ("PRODUCT"))
388               || (code11 && istag ("NAME")))
389             {
390               if (s && !s->name)
391                 {
392                   /* this solvable was created without seeing a
393                      PRODUCT entry, just set the name and continue */
394                   s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
395                   continue;
396                 }
397               if (s)
398                 {
399                   /* finish old solvable */
400                   if (!s->arch)
401                     s->arch = ARCH_NOARCH;
402                   if (!s->evr)
403                     s->evr = ID_EMPTY;
404                   if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
405                     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
406                   if (code10)
407                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
408                 }
409               /* create new solvable */
410               s = pool_id2solvable(pool, repo_add_solvable(repo));
411               repodata_extend(data, s - pool->solvables);
412               handle = s - pool->solvables;
413               s->name = pool_str2id(pool, join(&pd, "product", ":", value), 1);
414               if (datadir)
415                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DATADIR, datadir);
416               if (descrdir)
417                 repodata_set_str(data, s - pool->solvables, SUSETAGS_DESCRDIR, descrdir);
418               if (defvendor)
419                 s->vendor = pool_str2id(pool, defvendor, 1);
420               continue;
421             }
422
423           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
424              from here on.  */
425           if (!s)
426             {
427               s = pool_id2solvable(pool, repo_add_solvable(repo));
428               repodata_extend(data, s - pool->solvables);
429               handle = s - pool->solvables;
430             }
431
432           if (istag ("VERSION"))
433             pd.tmpvers = solv_strdup(value);
434           else if (istag ("RELEASE"))
435             pd.tmprel = solv_strdup(value);
436           else if (code11 && istag ("DISTRIBUTION"))
437             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
438           else if (istag ("UPDATEURLS"))
439             add_multiple_urls(data, handle, value, pool_str2id(pool, "update", 1));
440           else if (istag ("EXTRAURLS"))
441             add_multiple_urls(data, handle, value, pool_str2id(pool, "extra", 1));
442           else if (istag ("OPTIONALURLS"))
443             add_multiple_urls(data, handle, value, pool_str2id(pool, "optional", 1));
444           else if (istag ("RELNOTESURL"))
445             add_multiple_urls(data, handle, value, pool_str2id(pool, "releasenotes", 1));
446           else if (istag ("SHORTLABEL"))
447             repodata_set_str(data, s - pool->solvables, PRODUCT_SHORTLABEL, value);
448           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
449             repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, value);
450           else if (!strncmp (key, "LABEL.", 6))
451             repodata_set_str(data, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
452           else if (istag ("FLAGS"))
453             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
454           else if (istag ("VENDOR"))    /* actually already handled above */
455             s->vendor = pool_str2id(pool, value, 1);
456           else if (istag ("BASEARCHS"))
457             {
458               char *arch;
459
460               if ((arch = splitword(&value)) != 0)
461                 {
462                   s->arch = pool_str2id(pool, arch, 1);
463                   while ((arch = splitword(&value)) != 0)
464                     {
465                        otherarchs = solv_extend(otherarchs, numotherarchs, 1, sizeof(Id), 7);
466                        otherarchs[numotherarchs++] = pool_str2id(pool, arch, 1);
467                     }
468                 }
469             }
470           if (!code10)
471             continue;
472
473           /*
474            * Every tag below is Code10 only
475            *
476            */
477
478           if (istag ("ARCH"))
479             /* Theoretically we want to have the best arch of the given
480                modifiers which still is compatible with the system
481                arch.  We don't know the latter here, though.  */
482             s->arch = ARCH_NOARCH;
483           else if (istag ("PREREQUIRES"))
484             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
485           else if (istag ("REQUIRES"))
486             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
487           else if (istag ("PROVIDES"))
488             s->provides = adddep(pool, &pd, s->provides, value, 0);
489           else if (istag ("CONFLICTS"))
490             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
491           else if (istag ("OBSOLETES"))
492             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
493           else if (istag ("RECOMMENDS"))
494             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
495           else if (istag ("SUGGESTS"))
496             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
497           else if (istag ("SUPPLEMENTS"))
498             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
499           else if (istag ("ENHANCES"))
500             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
501           /* FRESHENS doesn't seem to exist.  */
502           else if (istag ("TYPE"))
503             repodata_set_str(data, s - pool->solvables, PRODUCT_TYPE, value);
504
505           /* XXX do something about LINGUAS and ARCH?
506           * <ma>: Don't think so. zypp does not use or propagate them.
507           */
508 #undef istag
509         }
510       else
511         pool_debug(pool, SOLV_ERROR, "repo_content: malformed line: %s\n", line);
512     }
513
514   if (datadir)
515     free(datadir);
516   if (descrdir)
517     free(descrdir);
518   if (defvendor)
519     free(defvendor);
520
521   if (s && !s->name)
522     {
523       pool_debug(pool, SOLV_FATAL, "repo_content: 'content' incomplete, no product solvable created!\n");
524       repo_free_solvable(repo, s - pool->solvables, 1);
525       s = 0;
526     }
527   if (s)
528     {
529       if (pd.tmprel)
530         s->evr = makeevr(pool, join(&pd, pd.tmpvers, "-", pd.tmprel));
531       else
532         s->evr = makeevr(pool, pd.tmpvers);
533       pd.tmpvers = solv_free((void *)pd.tmpvers);
534       pd.tmprel = solv_free((void *)pd.tmprel);
535
536       if (!s->arch)
537         s->arch = ARCH_NOARCH;
538       if (!s->evr)
539         s->evr = ID_EMPTY;
540       if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
541         s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
542       if (code10)
543         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
544
545       /* now for every other arch, clone the product except the architecture */
546       for (i = 0; i < numotherarchs; ++i)
547         {
548           Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
549           repodata_extend(data, p - pool->solvables);
550           p->name = s->name;
551           p->evr = s->evr;
552           p->vendor = s->vendor;
553           p->arch = otherarchs[i];
554
555           /* self provides */
556           if (s->name && p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
557               p->provides = repo_addid_dep(repo, p->provides, pool_rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
558
559           /* now merge the attributes */
560           repodata_merge_attrs(data, p - pool->solvables, s - pool->solvables);
561         }
562     }
563
564   if (pd.tmp)
565     solv_free(pd.tmp);
566   solv_free(line);
567   solv_free(otherarchs);
568   if (!(flags & REPO_NO_INTERNALIZE))
569     repodata_internalize(data);
570   return res;
571 }