try to move split and join out
[platform/upstream/libsolv.git] / tools / repo_rpmmd.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <sys/types.h>
9 #include <limits.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <expat.h>
15
16 #include "pool.h"
17 #include "repo.h"
18 #include "repo_utils.h"
19 #include "repo_rpmmd.h"
20
21
22 enum state {
23   STATE_START,
24   STATE_METADATA,
25   STATE_SOLVABLE,
26   STATE_PRODUCT,
27   STATE_PATTERN,
28   STATE_PATCH,
29   STATE_NAME,
30   STATE_ARCH,
31   STATE_VERSION,
32   STATE_FORMAT,
33   STATE_VENDOR,
34   STATE_PROVIDES,
35   STATE_PROVIDESENTRY,
36   STATE_PROVIDESCAP,
37   STATE_REQUIRES,
38   STATE_REQUIRESENTRY,
39   STATE_REQUIRESCAP,
40   STATE_OBSOLETES,
41   STATE_OBSOLETESENTRY,
42   STATE_OBSOLETESCAP,
43   STATE_CONFLICTS,
44   STATE_CONFLICTSENTRY,
45   STATE_CONFLICTSCAP,
46   STATE_RECOMMENDS,
47   STATE_RECOMMENDSENTRY,
48   STATE_RECOMMENDSCAP,
49   STATE_SUPPLEMENTS,
50   STATE_SUPPLEMENTSENTRY,
51   STATE_SUPPLEMENTSCAP,
52   STATE_SUGGESTS,
53   STATE_SUGGESTSENTRY,
54   STATE_SUGGESTSCAP,
55   STATE_ENHANCES,
56   STATE_ENHANCESENTRY,
57   STATE_ENHANCESCAP,
58   STATE_FRESHENS,
59   STATE_FRESHENSENTRY,
60   STATE_FRESHENSCAP,
61   STATE_FILE,
62   STATE_SUMMARY,
63   STATE_DESCRIPTION,
64   NUMSTATES
65 };
66
67
68 struct stateswitch {
69   enum state from;
70   char *ename;
71   enum state to;
72   int docontent;
73 };
74
75 static struct stateswitch stateswitches[] = {
76
77   { STATE_START,       "product",         STATE_SOLVABLE, 0 },
78   { STATE_START,       "pattern",         STATE_SOLVABLE, 0 },
79   { STATE_START,       "patch",           STATE_SOLVABLE, 0 },
80
81   { STATE_START,       "metadata",        STATE_METADATA, 0 },
82   { STATE_METADATA,    "package",         STATE_SOLVABLE, 0 },
83   
84   { STATE_SOLVABLE,    "name",            STATE_NAME, 1 },
85   { STATE_SOLVABLE,    "arch",            STATE_ARCH, 1 },
86   { STATE_SOLVABLE,    "version",         STATE_VERSION, 0 },
87   { STATE_SOLVABLE,    "vendor",          STATE_VENDOR, 1 },
88
89   { STATE_SOLVABLE,    "format",          STATE_FORMAT, 0 },
90
91   /* those are used in libzypp xml store */
92   { STATE_SOLVABLE,    "provides",        STATE_PROVIDES, 0 },
93   { STATE_SOLVABLE,    "requires",        STATE_REQUIRES, 0 },
94   { STATE_SOLVABLE,    "obsoletes",       STATE_OBSOLETES , 0 },
95   { STATE_SOLVABLE,    "conflicts",       STATE_CONFLICTS , 0 },
96   { STATE_SOLVABLE,    "recommends",      STATE_RECOMMENDS , 0 },
97   { STATE_SOLVABLE,    "supplements",     STATE_SUPPLEMENTS, 0 },
98   { STATE_SOLVABLE,    "suggests",        STATE_SUGGESTS, 0 },
99   { STATE_SOLVABLE,    "enhances",        STATE_ENHANCES, 0 },
100   { STATE_SOLVABLE,    "freshens",        STATE_FRESHENS, 0 },
101
102   { STATE_PROVIDES,    "capability",      STATE_PROVIDESCAP, 1 },
103   { STATE_REQUIRES,    "capability",      STATE_REQUIRESCAP, 1 },
104   { STATE_OBSOLETES,   "capability",      STATE_OBSOLETESCAP, 1 },
105   { STATE_CONFLICTS,   "capability",      STATE_CONFLICTSCAP, 1 },
106   { STATE_RECOMMENDS,  "capability",      STATE_RECOMMENDSCAP, 1 },
107   { STATE_SUPPLEMENTS, "capability",      STATE_SUPPLEMENTSCAP, 1 },
108   { STATE_SUGGESTS,    "capability",      STATE_SUGGESTSCAP, 1 },
109   { STATE_ENHANCES,    "capability",      STATE_ENHANCESCAP, 1 },
110   { STATE_FRESHENS,    "capability",      STATE_FRESHENSCAP, 1 },
111   
112   { STATE_SOLVABLE,    "summary",         STATE_SUMMARY, 1 },
113   { STATE_SOLVABLE,    "description",     STATE_DESCRIPTION, 1 },
114
115   { STATE_FORMAT,      "rpm:vendor",      STATE_VENDOR, 1 },
116
117   /* rpm-md dependencies */ 
118   { STATE_FORMAT,      "rpm:provides",    STATE_PROVIDES, 0 },
119   { STATE_FORMAT,      "rpm:requires",    STATE_REQUIRES, 0 },
120   { STATE_FORMAT,      "rpm:obsoletes",   STATE_OBSOLETES , 0 },
121   { STATE_FORMAT,      "rpm:conflicts",   STATE_CONFLICTS , 0 },
122   { STATE_FORMAT,      "rpm:recommends",  STATE_RECOMMENDS , 0 },
123   { STATE_FORMAT,      "rpm:supplements", STATE_SUPPLEMENTS, 0 },
124   { STATE_FORMAT,      "rpm:suggests",    STATE_SUGGESTS, 0 },
125   { STATE_FORMAT,      "rpm:enhances",    STATE_ENHANCES, 0 },
126   { STATE_FORMAT,      "rpm:freshens",    STATE_FRESHENS, 0 },
127   { STATE_FORMAT,      "file",            STATE_FILE, 1 },
128   { STATE_PROVIDES,    "rpm:entry",       STATE_PROVIDESENTRY, 0 },
129   { STATE_REQUIRES,    "rpm:entry",       STATE_REQUIRESENTRY, 0 },
130   { STATE_OBSOLETES,   "rpm:entry",       STATE_OBSOLETESENTRY, 0 },
131   { STATE_CONFLICTS,   "rpm:entry",       STATE_CONFLICTSENTRY, 0 },
132   { STATE_RECOMMENDS,  "rpm:entry",       STATE_RECOMMENDSENTRY, 0 },
133   { STATE_SUPPLEMENTS, "rpm:entry",       STATE_SUPPLEMENTSENTRY, 0 },
134   { STATE_SUGGESTS,    "rpm:entry",       STATE_SUGGESTSENTRY, 0 },
135   { STATE_ENHANCES,    "rpm:entry",       STATE_ENHANCESENTRY, 0 },
136   { STATE_FRESHENS,    "rpm:entry",       STATE_FRESHENSENTRY, 0 },
137   { NUMSTATES}
138 };
139
140 struct parsedata {
141   struct parsedata_common common;
142   char *kind;
143   int depth;
144   enum state state;
145   int statedepth;
146   char *content;
147   int lcontent;
148   int acontent;
149   int docontent;
150   int numpacks;
151   Solvable *solvable;
152   struct stateswitch *swtab[NUMSTATES];
153   enum state sbtab[NUMSTATES];
154   const char *lang;
155   const char *capkind;
156 };
157
158 static char *flagtabnum[] = {
159   ">",
160   "=",
161   ">=",
162   "<",
163   "!=",
164   "<=",
165   "=="
166 };
167
168 /**
169  * adds plain dependencies, that is strings like "foo > 2.0"
170  * which are used in libzypp xml store, not in rpm-md.
171  */
172 static unsigned int
173 adddepplain(Pool *pool, struct parsedata_common *pd, unsigned int olddeps, char *line, Id marker, char *kind)
174 {
175   int i, flags;
176   Id id, evrid;
177   char *sp[4];
178
179   i = split(line + 5, sp, 4);
180   if (i != 1 && i != 3)
181     {
182       fprintf(stderr, "Bad dependency line: %s\n", line);
183       exit(1);
184     }
185   if (kind)
186     id = str2id(pool, join(pd, kind, ":", sp[0]), 1);
187   else
188     id = str2id(pool, sp[0], 1);
189   if (i == 3)
190     {
191       evrid = makeevr(pool, sp[2]);
192       for (flags = 0; flags < 6; flags++)
193         if (!strcmp(sp[1], flagtabnum[flags]))
194           break;
195       if (flags == 7)
196         {
197           fprintf(stderr, "Unknown relation '%s'\n", sp[1]);
198           exit(1);
199         }
200       id = rel2id(pool, id, evrid, flags + 1, 1);
201     }
202   return repo_addid_dep(pd->repo, olddeps, id, marker);
203 }
204
205 static Id
206 makeevr_atts(Pool *pool, struct parsedata *pd, const char **atts)
207 {
208   const char *e, *v, *r, *v2;
209   char *c;
210   int l;
211
212   e = v = r = 0;
213   for (; *atts; atts += 2)
214     {
215       if (!strcmp(*atts, "epoch"))
216         e = atts[1];
217       else if (!strcmp(*atts, "ver"))
218         v = atts[1];
219       else if (!strcmp(*atts, "rel"))
220         r = atts[1];
221     }
222   if (e && !strcmp(e, "0"))
223     e = 0;
224   if (v && !e)
225     {
226       for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)
227         ;
228       if (v2 > v && *v2 == ':')
229         e = "0";
230     }
231   l = 1;
232   if (e)
233     l += strlen(e) + 1;
234   if (v)
235     l += strlen(v);
236   if (r)
237     l += strlen(r) + 1;
238   if (l > pd->acontent)
239     {
240       pd->content = sat_realloc(pd->content, l + 256);
241       pd->acontent = l + 256;
242     }
243   c = pd->content;
244   if (e)
245     {
246       strcpy(c, e);
247       c += strlen(c);
248       *c++ = ':';
249     }
250   if (v)
251     {
252       strcpy(c, v);
253       c += strlen(c);
254     }
255   if (r)
256     {
257       *c++ = '-';
258       strcpy(c, r);
259       c += strlen(c);
260     }
261   *c = 0;
262   if (!*pd->content)
263     return 0;
264 #if 0
265   fprintf(stderr, "evr: %s\n", pd->content);
266 #endif
267   return str2id(pool, pd->content, 1);
268 }
269
270 static const char *
271 find_attr(const char *txt, const char **atts)
272 {
273   char *k;
274   k = 0;
275   for (; *atts; atts += 2)
276     {
277       if (!strcmp(*atts, txt))
278         return atts[1];
279     }
280   return k;
281 }
282
283 static char *flagtab[] = {
284   "GT",
285   "EQ",
286   "GE",
287   "LT",
288   "NE",
289   "LE"
290 };
291
292 static unsigned int
293 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
294 {
295   Id id, name, marker;
296   const char *n, *f, *k;
297   const char **a;
298
299   n = f = k = 0;
300   marker = isreq ? -SOLVABLE_PREREQMARKER : 0;
301   for (a = atts; *a; a += 2)
302     {
303       if (!strcmp(*a, "name"))
304         n = a[1];
305       else if (!strcmp(*a, "flags"))
306         f = a[1];
307       else if (!strcmp(*a, "kind"))
308         k = a[1];
309       else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
310         marker = SOLVABLE_PREREQMARKER;
311     }
312   if (!n)
313     return olddeps;
314   if (k && !strcmp(k, "package"))
315     k = 0;
316   if (k)
317     {
318       int l = strlen(k) + 1 + strlen(n) + 1;
319       if (l > pd->acontent)
320         {
321           pd->content = sat_realloc(pd->content, l + 256);
322           pd->acontent = l + 256;
323         }
324       sprintf(pd->content, "%s:%s", k, n); 
325       name = str2id(pool, pd->content, 1); 
326     }
327   else
328     name = str2id(pool, (char *)n, 1);
329   if (f)
330     {
331       Id evr = makeevr_atts(pool, pd, atts);
332       int flags;
333       for (flags = 0; flags < 6; flags++)
334         if (!strcmp(f, flagtab[flags]))
335           break;
336       flags = flags < 6 ? flags + 1 : 0;
337       id = rel2id(pool, name, evr, flags, 1);
338     }
339   else
340     id = name;
341 #if 0
342   fprintf(stderr, "new dep %s%s%s\n", id2str(pool, d), id2rel(pool, d), id2evr(pool, d));
343 #endif
344   return repo_addid_dep(pd->common.repo, olddeps, id, marker);
345 }
346
347
348 static void XMLCALL
349 startElement(void *userData, const char *name, const char **atts)
350 {
351   fprintf(stderr,"+tag: %s\n", name);
352   struct parsedata *pd = userData;
353   Pool *pool = pd->common.pool;
354   Solvable *s = pd->solvable;
355   struct stateswitch *sw;
356
357   if (pd->depth != pd->statedepth)
358     {
359       pd->depth++;
360       return;
361     }
362   pd->depth++;
363   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
364     if (!strcmp(sw->ename, name))
365       break;
366   if (sw->from != pd->state)
367     {
368 //#if 0
369       fprintf(stderr, "into unknown: %s\n", name);
370 //#endif
371       return;
372     }
373   pd->state = sw->to;
374   pd->docontent = sw->docontent;
375   pd->statedepth = pd->depth;
376   pd->lcontent = 0;
377   *pd->content = 0;
378   switch(pd->state)
379     {
380     case STATE_METADATA:
381       for (; *atts; atts += 2)
382         {
383           if (!strcmp(*atts, "packages"))
384             {
385               pd->numpacks = atoi(atts[1]);
386               if (pd->numpacks < 0)
387                 pd->numpacks = 0;
388 #if 0
389               fprintf(stderr, "numpacks: %d\n", pd->numpacks);
390 #endif
391               pd->solvable = pool_id2solvable(pool, repo_add_solvable_block(pd->common.repo, pd->numpacks));
392             }
393         }
394       break;
395     case STATE_SOLVABLE:
396       pd->kind = 0;
397       if ( name[2] == 't' && name[3] == 't' )
398         pd->kind = "pattern";
399       else if ( name[1] == 'r' )
400         pd->kind = "product";
401       else if ( name[2] == 't' && name[3] == 'c' )
402         pd->kind = "patch";
403       
404       if (pd->numpacks == 0)
405         pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->common.repo));
406 #if 0
407       fprintf(stderr, "package #%d\n", pd->solvable - pool->solvables);
408 #endif
409       break;
410     case STATE_VERSION:
411       s->evr = makeevr_atts(pool, pd, atts);
412       break;
413     case STATE_PROVIDES:
414       s->provides = 0;
415       break;
416     case STATE_PROVIDESENTRY:
417       s->provides = adddep(pool, pd, s->provides, atts, 0);
418       break;
419     case STATE_REQUIRES:
420       s->requires = 0;
421       break;
422     case STATE_REQUIRESENTRY:
423       s->requires = adddep(pool, pd, s->requires, atts, 1);
424       break;
425     case STATE_OBSOLETES:
426       s->obsoletes = 0;
427       break;
428     case STATE_OBSOLETESENTRY:
429       s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
430       break;
431     case STATE_CONFLICTS:
432       s->conflicts = 0;
433       break;
434     case STATE_CONFLICTSENTRY:
435       s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
436       break;
437     case STATE_RECOMMENDS:
438       s->recommends = 0;
439       break;
440     case STATE_RECOMMENDSENTRY:
441       s->recommends = adddep(pool, pd, s->recommends, atts, 0);
442       break;
443     case STATE_SUPPLEMENTS:
444       s->supplements= 0;
445       break;
446     case STATE_SUPPLEMENTSENTRY:
447       s->supplements = adddep(pool, pd, s->supplements, atts, 0);
448       break;
449     case STATE_SUGGESTS:
450       s->suggests = 0;
451       break;
452     case STATE_SUGGESTSENTRY:
453       s->suggests = adddep(pool, pd, s->suggests, atts, 0);
454       break;
455     case STATE_ENHANCES:
456       s->enhances = 0;
457       break;
458     case STATE_ENHANCESENTRY:
459       s->enhances = adddep(pool, pd, s->enhances, atts, 0);
460       break;
461     case STATE_FRESHENS:
462       s->freshens = 0;
463       break;
464     case STATE_FRESHENSENTRY:
465       s->freshens = adddep(pool, pd, s->freshens, atts, 0);
466       break;
467     case STATE_PROVIDESCAP:
468     case STATE_REQUIRESCAP:
469     case STATE_OBSOLETESCAP:
470     case STATE_CONFLICTSCAP:
471     case STATE_RECOMMENDSCAP:
472     case STATE_SUPPLEMENTSCAP:
473     case STATE_SUGGESTSCAP:
474     case STATE_ENHANCESCAP:
475     case STATE_FRESHENSCAP:
476       pd->capkind = find_attr("kind", atts);
477       //fprintf(stderr,"capkind es: %s\n", pd->capkind);
478       break;
479     case STATE_SUMMARY:
480       pd->lang = find_attr("lang", atts);
481       break;
482     case STATE_DESCRIPTION:
483       pd->lang = find_attr("lang", atts);
484       break;
485     default:
486       break;
487     }
488 }
489
490 static void XMLCALL
491 endElement(void *userData, const char *name)
492 {
493   fprintf(stderr,"-tag: %s\n", name);
494   struct parsedata *pd = userData;
495   Pool *pool = pd->common.pool;
496   Solvable *s = pd->solvable;
497   Repo *repo = pd->common.repo;
498   Id id;
499
500   if (pd->depth != pd->statedepth)
501     {
502       pd->depth--;
503       // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
504       return;
505     }
506   pd->depth--;
507   pd->statedepth--;
508   switch (pd->state)
509     {
510     case STATE_PATTERN:
511     case STATE_PRODUCT:
512     case STATE_SOLVABLE:
513       if (!s->arch)
514         s->arch = ARCH_NOARCH;
515       if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
516         s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
517       s->supplements = repo_fix_legacy(repo, s->provides, s->supplements);
518       if (pd->numpacks > 0)
519         {
520           pd->numpacks--;
521           pd->solvable++;
522         }
523       pd->kind = 0;
524       break;
525     case STATE_NAME:
526       if ( pd->kind )
527           s->name = str2id(pool, join(&pd->common, pd->kind, ":", pd->content), 1);
528       else
529           s->name = str2id(pool, pd->content, 1);
530       break;
531     case STATE_ARCH:
532       s->arch = str2id(pool, pd->content, 1);
533       break;
534     case STATE_VENDOR:
535       s->vendor = str2id(pool, pd->content, 1);
536       break;
537     case STATE_FILE:
538       id = str2id(pool, pd->content, 1);
539       s->provides = repo_addid(repo, s->provides, id);
540       break;
541     case STATE_PROVIDESCAP:
542       s->provides = adddepplain(pool, &pd->common, s->provides, pd->content, 0, 0);
543       break;
544     case STATE_REQUIRESCAP:
545       s->requires = adddepplain(pool, &pd->common, s->requires, pd->content, 0, 0);
546       break;
547     case STATE_OBSOLETESCAP:
548       s->obsoletes = adddepplain(pool, &pd->common, s->obsoletes, pd->content, 0, 0);
549       break;
550     case STATE_CONFLICTSCAP:
551       s->conflicts = adddepplain(pool, &pd->common, s->conflicts, pd->content, 0, 0);
552       break;
553     case STATE_RECOMMENDSCAP:
554       s->recommends = adddepplain(pool, &pd->common, s->recommends, pd->content, 0, 0);
555       break;
556     case STATE_SUPPLEMENTSCAP:
557       s->supplements = adddepplain(pool, &pd->common, s->supplements, pd->content, 0, 0);
558       break;
559     case STATE_SUGGESTSCAP:
560       s->suggests = adddepplain(pool, &pd->common, s->suggests, pd->content, 0, 0);
561       break;
562     case STATE_ENHANCESCAP:
563       s->enhances = adddepplain(pool, &pd->common, s->enhances, pd->content, 0, 0);
564       break;
565     case STATE_FRESHENSCAP:
566       s->freshens = adddepplain(pool, &pd->common, s->freshens, pd->content, 0, 0);
567       break;
568     case STATE_SUMMARY:
569       pd->lang = 0;
570       break;
571     case STATE_DESCRIPTION:
572       pd->lang = 0;
573       break;
574     default:
575       break;
576     }
577   pd->state = pd->sbtab[pd->state];
578   pd->docontent = 0;
579   fprintf(stderr, "back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
580 }
581
582 static void XMLCALL
583 characterData(void *userData, const XML_Char *s, int len)
584 {
585   struct parsedata *pd = userData;
586   int l;
587   char *c;
588
589   if (!pd->docontent)
590     return;
591   l = pd->lcontent + len + 1;
592   if (l > pd->acontent)
593     {
594       pd->content = sat_realloc(pd->content, l + 256);
595       pd->acontent = l + 256;
596     }
597   c = pd->content + pd->lcontent;
598   pd->lcontent += len;
599   while (len-- > 0)
600     *c++ = *s++;
601   *c = 0;
602 }
603
604
605 #define BUFF_SIZE 8192
606
607 void
608 repo_add_rpmmd(Repo *repo, FILE *fp)
609 {
610   Pool *pool = repo->pool;
611   struct parsedata pd;
612   char buf[BUFF_SIZE];
613   int i, l;
614   struct stateswitch *sw;
615
616   memset(&pd, 0, sizeof(pd));
617   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
618     {
619       if (!pd.swtab[sw->from])
620         pd.swtab[sw->from] = sw;
621       pd.sbtab[sw->to] = sw->from;
622     }
623   pd.common.pool = pool;
624   pd.common.repo = repo;
625   pd.content = sat_malloc(256);
626   pd.acontent = 256;
627   pd.lcontent = 0;
628   pd.common.tmp = 0;
629   pd.common.tmpl = 0;
630   pd.kind = 0;
631   XML_Parser parser = XML_ParserCreate(NULL);
632   XML_SetUserData(parser, &pd);
633   XML_SetElementHandler(parser, startElement, endElement);
634   XML_SetCharacterDataHandler(parser, characterData);
635   for (;;)
636     {
637       l = fread(buf, 1, sizeof(buf), fp);
638       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
639         {
640           fprintf(stderr, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
641           exit(1);
642         }
643       if (l == 0)
644         break;
645     }
646   XML_ParserFree(parser);
647   if (pd.numpacks)
648     repo_free_solvable_block(repo, pd.solvable - pool->solvables, pd.numpacks, 1);
649   sat_free(pd.content);
650 }