make url ids extensible and fix parsers.
[platform/upstream/libsolv.git] / tools / 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
28 /*
29  * split l into m parts, store to sp[]
30  *  split at whitespace
31  */
32
33 static int
34 split(char *l, char **sp, int m)
35 {
36   int i;
37   for (i = 0; i < m;)
38     {
39       while (*l == ' ' || *l == '\t')
40         l++;
41       if (!*l)
42         break;
43       sp[i++] = l;
44       if (i == m)
45         break;
46       while (*l && !(*l == ' ' || *l == '\t'))
47         l++;
48       if (!*l)
49         break;
50       *l++ = 0;
51     }
52   return i;
53 }
54
55
56 struct parsedata {
57   Repo *repo;
58   char *tmp;
59   int tmpl;
60 };
61
62
63 static Id
64 makeevr(Pool *pool, char *s)
65 {
66   if (!strncmp(s, "0:", 2) && s[2])
67     s += 2;
68   return str2id(pool, s, 1);
69 }
70
71 /*
72  * dependency relations
73  */
74
75 static char *flagtab[] = {
76   ">",
77   "=",
78   ">=",
79   "<",
80   "!=",
81   "<="
82 };
83
84
85 /*
86  * join up to three strings into one
87  */
88
89 static char *
90 join(struct parsedata *pd, const char *s1, const char *s2, const char *s3)
91 {
92   int l = 1;
93   char *p;
94
95   if (s1)
96     l += strlen(s1);
97   if (s2)
98     l += strlen(s2);
99   if (s3)
100     l += strlen(s3);
101   if (l > pd->tmpl)
102     {
103       pd->tmpl = l + 256;
104       pd->tmp = sat_realloc(pd->tmp, pd->tmpl);
105     }
106   p = pd->tmp;
107   if (s1)
108     {
109       strcpy(p, s1);
110       p += strlen(s1);
111     }
112   if (s2)
113     {
114       strcpy(p, s2);
115       p += strlen(s2);
116     }
117   if (s3)
118     {
119       strcpy(p, s3);
120       p += strlen(s3);
121     }
122   return pd->tmp;
123 }
124
125
126 /*
127  * add dependency to pool
128  */
129
130 static unsigned int
131 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, char *line, Id marker)
132 {
133   int flags, words;
134   Id id, evrid;
135   char *sp[4];
136
137   words = 0;
138   while (1)
139     {
140       /* Name [relop evr] [rest] --> 1, 2, 3 or 4 fields.  */
141       if ( line )
142         {
143           words += split(line, sp + words, 4 - words);
144           line = 0;
145         }
146       /* Hack, as the content file adds 'package:' for package
147          dependencies sometimes.  */
148       if (!strncmp (sp[0], "package:", 8))
149         sp[0] += 8;
150       id = str2id(pool, sp[0], 1);
151       if (words >= 3 && strpbrk (sp[1], "<>="))
152         {
153           evrid = makeevr(pool, sp[2]);
154           for (flags = 0; flags < 6; flags++)
155             if (!strcmp(sp[1], flagtab[flags]))
156               break;
157           if (flags == 6)
158             {
159               fprintf(stderr, "Unknown relation '%s'\n", sp[1]);
160               exit(1);
161             }
162           id = rel2id(pool, id, evrid, flags + 1, 1);
163           /* Consume three words, there's nothing to move to front.  */
164           if (words == 4)
165             line = sp[3];
166           words = 0;
167         }
168       else
169         {
170           int j;
171           /* Consume one word.  If we had more move them to front.  */
172           words--;
173           for (j = 0; j < words; j++)
174             sp[j] = sp[j+1];
175           if (words == 3)
176             line = sp[2], words = 2;
177         }
178       olddeps = repo_addid_dep(pd->repo, olddeps, id, marker);
179       if (! ( line || words > 0 ) )
180         break;
181     }
182   return olddeps;
183 }
184
185
186 /*
187  * split value and add to pool
188  */
189
190 static void
191 add_multiple_strings(Repodata *data, Id handle, Id name, char *value)
192 {
193   char *sp[2];
194   while (value)
195     {
196       int words = split(value, sp, 2);
197       if (!words)
198         break;
199       repodata_add_poolstr_array(data, handle, name, sp[0]);
200       if (words == 1)
201         break;
202       value = sp[1];
203     }
204 }
205
206 /*
207  * split value and add to pool
208  */
209
210 static void
211 add_multiple_urls(Repodata *data, Id handle, char *value, Id type)
212 {
213   char *sp[2];
214   while (value)
215     {
216       int words = split(value, sp, 2);
217       if (!words)
218         break;
219       repodata_add_poolstr_array(data, handle, PRODUCT_URL, sp[0]);
220       repodata_add_idarray(data, handle, PRODUCT_URL_TYPE, type);
221       if (words == 1)
222         break;
223       value = sp[1];
224     }
225 }
226
227
228
229 /*
230  * add 'content' to repo
231  *
232  */
233
234 void
235 repo_add_content(Repo *repo, FILE *fp)
236 {
237   Pool *pool = repo->pool;
238   char *line, *linep;
239   int aline;
240   Solvable *s, *firsts = 0;
241   struct parsedata pd;
242   Repodata *data;
243   Id handle = 0;
244   int contentstyle = 0;
245
246   int i = 0;
247
248   /* architectures
249      we use the first architecture in BASEARCHS or noarch
250      for the product. At the end we create (clone) the product
251      for each one of the remaining architectures
252      we allow max 4 archs 
253   */
254   unsigned int numotherarchs = 0;
255   Id otherarchs[4];
256   for ( ; i < 4; ++i )
257     otherarchs[i] = 0;
258
259   memset(&pd, 0, sizeof(pd));
260   line = sat_malloc(1024);
261   aline = 1024;
262
263   if (repo->nrepodata)
264     /* use last repodata */
265     data = repo->repodata + repo->nrepodata - 1;
266   else
267     data = repo_add_repodata(repo, 0);
268
269   pd.repo = repo;
270   linep = line;
271   s = 0;
272
273   for (;;)
274     {
275       char *fields[2];
276
277       /* read line into big-enough buffer */
278       if (linep - line + 16 > aline)
279         {
280           aline = linep - line;
281           line = sat_realloc(line, aline + 512);
282           linep = line + aline;
283           aline += 512;
284         }
285       if (!fgets(linep, aline - (linep - line), fp))
286         break;
287       linep += strlen(linep);
288       if (linep == line || linep[-1] != '\n')
289         continue;
290       *--linep = 0;
291       linep = line;
292
293       /* expect "key value" lines */
294       if (split (line, fields, 2) == 2)
295         {
296           char *key = fields[0];
297           char *value = fields[1];
298 #if 0
299           fprintf (stderr, "key %s, value %s\n", key, fields[1]);
300 #endif
301
302 #define istag(x) (!strcmp (key, x))
303 #define code10 (contentstyle == 10)
304 #define code11 (contentstyle == 11)
305
306           if (contentstyle == 0)
307             {
308               if (istag ("CONTENTSTYLE"))
309                 {
310                   contentstyle = atoi(value);
311                   continue;
312                 }
313               else
314                 contentstyle = 10;
315             }
316
317           if ((code10 && istag ("PRODUCT"))
318               || (code11 && istag ("NAME")))
319             {
320               /* Finish old solvable, but only if it wasn't created
321                  on demand without seeing a PRODUCT entry.  */
322               if (!firsts)
323                 {
324                   if (s && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
325                     s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
326                   if (s && code10)
327                     s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
328                   /* first product.  */
329                   s = pool_id2solvable(pool, repo_add_solvable(repo));
330                   repodata_extend(data, s - pool->solvables);
331                   handle = repodata_get_handle(data, s - pool->solvables - repo->start);
332                 }
333               firsts = 0;
334               s->name = str2id(pool, join(&pd, "product", ":", value), 1);
335               continue;
336             }
337
338           /* Sometimes PRODUCT/NAME is not the first entry, but we need a solvable
339              from here on.  */
340           if (!s)
341             {
342               firsts = s = pool_id2solvable(pool, repo_add_solvable(repo));
343               repodata_extend(data, s - pool->solvables);
344               handle = repodata_get_handle(data, s - pool->solvables - repo->start);
345             }
346
347           if (code11 && istag ("REFERENCES"))
348             repo_set_id(repo, s - pool->solvables, PRODUCT_REFERENCES, str2id(pool, value, 1));
349           else if (istag ("VERSION"))
350             s->evr = makeevr(pool, value);
351           else if (code11 && istag ("DISTRIBUTION"))
352             repo_set_str(repo, s - pool->solvables, SOLVABLE_DISTRIBUTION, value);
353           else if (code11 && istag ("FLAVOR"))
354             repo_set_str(repo, s - pool->solvables, PRODUCT_FLAVOR, value);
355           else if (istag ("DATADIR"))
356             repo_set_str(repo, s - pool->solvables, SUSETAGS_DATADIR, value);
357           else if (istag ("UPDATEURLS"))
358             add_multiple_urls(data, handle, value, PRODUCT_URL_TYPE_UPDATE);
359           else if (istag ("EXTRAURLS"))
360             add_multiple_urls(data, handle, value, PRODUCT_URL_TYPE_EXTRA);
361           else if (istag ("OPTIONALURLS"))
362             add_multiple_urls(data, handle, value, PRODUCT_URL_TYPE_OPTIONAL);
363           else if (istag ("RELNOTESURL"))
364             add_multiple_urls(data, handle, value, PRODUCT_URL_TYPE_RELNOTES);
365           else if (istag ("SHORTLABEL"))
366             repo_set_str(repo, s - pool->solvables, PRODUCT_SHORTLABEL, value);
367           else if (istag ("LABEL")) /* LABEL is the products SUMMARY. */
368             repo_set_str(repo, s - pool->solvables, SOLVABLE_SUMMARY, value);
369           else if (!strncmp (key, "LABEL.", 6))
370             repo_set_str(repo, s - pool->solvables, pool_id2langid(pool, SOLVABLE_SUMMARY, key + 6, 1), value);
371           else if (istag ("FLAGS"))
372             add_multiple_strings(data, handle, PRODUCT_FLAGS, value);
373           else if (istag ("VENDOR"))
374             s->vendor = str2id(pool, value, 1);
375           else if (istag ("BASEARCHS"))
376             {
377               char *sp[2];
378
379               /* get the first architecture and use it
380                    for the product arch */
381               int words = split(value, sp, 2);
382               if ( words > 0 )
383               {
384                 /* there is at least one architecture */
385                 s->arch = str2id(pool, sp[0], 1);
386                 /* ignore the rest for now */
387                 value = sp[1];
388
389                 while ( value && (numotherarchs < 5) )
390                   {
391                     words = split(value, sp, 2);
392                     if (!words)
393                       break;
394                     /* we have a new extra architecture */
395                     numotherarchs++;
396                     otherarchs[numotherarchs - 1 ] = str2id(pool, sp[0], 1); 
397                     if (words == 1)
398                       break;
399                     value = sp[1];
400                   }
401               }
402             }
403
404           /*
405            * Every tag below is Code10 only
406            *
407            */
408
409           else if (code10 && istag ("DISTPRODUCT"))
410             /* DISTPRODUCT is for registration and Yast, not for the solver. */
411             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTPRODUCT, value);
412           else if (code10 && istag ("DISTVERSION"))
413             /* DISTVERSION is for registration and Yast, not for the solver. */
414             repo_set_str(repo, s - pool->solvables, PRODUCT_DISTVERSION, value);
415           else if (code10 && istag ("ARCH"))
416             /* Theoretically we want to have the best arch of the given
417                modifiers which still is compatible with the system
418                arch.  We don't know the latter here, though.  */
419             s->arch = ARCH_NOARCH;
420           else if (code10 && istag ("PREREQUIRES"))
421             s->requires = adddep(pool, &pd, s->requires, value, SOLVABLE_PREREQMARKER);
422           else if (code10 && istag ("REQUIRES"))
423             s->requires = adddep(pool, &pd, s->requires, value, -SOLVABLE_PREREQMARKER);
424           else if (code10 && istag ("PROVIDES"))
425             s->provides = adddep(pool, &pd, s->provides, value, 0);
426           else if (code10 && istag ("CONFLICTS"))
427             s->conflicts = adddep(pool, &pd, s->conflicts, value, 0);
428           else if (code10 && istag ("OBSOLETES"))
429             s->obsoletes = adddep(pool, &pd, s->obsoletes, value, 0);
430           else if (code10 && istag ("RECOMMENDS"))
431             s->recommends = adddep(pool, &pd, s->recommends, value, 0);
432           else if (code10 && istag ("SUGGESTS"))
433             s->suggests = adddep(pool, &pd, s->suggests, value, 0);
434           else if (code10 && istag ("SUPPLEMENTS"))
435             s->supplements = adddep(pool, &pd, s->supplements, value, 0);
436           else if (code10 && istag ("ENHANCES"))
437             s->enhances = adddep(pool, &pd, s->enhances, value, 0);
438           /* FRESHENS doesn't seem to exist.  */
439           else if (code10 && istag ("TYPE"))
440             repo_set_str(repo, s - pool->solvables, PRODUCT_TYPE, value);
441
442           /* XXX do something about LINGUAS and ARCH?
443           * <ma>: Don't think so. zypp does not use or propagate them.
444           */
445 #undef istag
446         }
447       else
448         fprintf (stderr, "malformed line: %s\n", line);
449     }
450
451   if (!s)
452     {
453       fprintf(stderr, "No product solvable created !\n");
454       exit(1);
455     }
456
457   if (!s->arch)
458     s->arch = ARCH_NOARCH;
459   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
460     {
461       s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
462       if (code10)
463         s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
464     }
465   
466   /* now for every other arch, clone the product except the architecture */
467   for ( i=0; i < numotherarchs; ++i )
468     {
469       Solvable *p = pool_id2solvable(pool, repo_add_solvable(repo));
470       repodata_extend(data, p - pool->solvables);
471       /*handle = repodata_get_handle(data, p - pool->solvables - repo->start);*/
472       p->name = s->name;
473       p->evr = s->evr;
474       p->vendor = s->vendor;
475       p->arch = otherarchs[i];
476
477       /* self provides */
478       if (p->arch != ARCH_SRC && p->arch != ARCH_NOSRC)
479           p->provides = repo_addid_dep(repo, p->provides, rel2id(pool, p->name, p->evr, REL_EQ, 1), 0);
480
481       /* now merge the attributes */
482       repodata_merge_attrs(data, p - pool->solvables - repo->start, s - pool->solvables- repo->start);
483     }
484   
485   if (data)
486     repodata_internalize(data);
487
488   if (pd.tmp)
489     sat_free(pd.tmp);
490   sat_free(line);
491 }