- also disable update rules for "keep installed" jobs.
[platform/upstream/libsolv.git] / src / repo_helix.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 /*
9  * repo_helix.c
10  * 
11  * Parse 'helix' XML representation
12  * and create 'repo'
13  * 
14  */
15
16 #include <sys/types.h>
17 #include <limits.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <expat.h>
23
24 #include "repo_helix.h"
25 #include "evr.h"
26
27
28 /* XML parser states */
29
30 enum state {
31   STATE_START,
32   STATE_CHANNEL,
33   STATE_SUBCHANNEL,
34   STATE_PACKAGE,
35   STATE_NAME,
36   STATE_VENDOR,
37   STATE_BUILDTIME,
38   STATE_HISTORY,
39   STATE_UPDATE,
40   STATE_EPOCH,
41   STATE_VERSION,
42   STATE_RELEASE,
43   STATE_ARCH,
44   STATE_PROVIDES,
45   STATE_PROVIDESENTRY,
46   STATE_REQUIRES,
47   STATE_REQUIRESENTRY,
48   STATE_OBSOLETES,
49   STATE_OBSOLETESENTRY,
50   STATE_CONFLICTS,
51   STATE_CONFLICTSENTRY,
52   STATE_RECOMMENDS,
53   STATE_RECOMMENDSENTRY,
54   STATE_SUPPLEMENTS,
55   STATE_SUPPLEMENTSENTRY,
56   STATE_SUGGESTS,
57   STATE_SUGGESTSENTRY,
58   STATE_ENHANCES,
59   STATE_ENHANCESENTRY,
60   STATE_FRESHENS,
61   STATE_FRESHENSENTRY,
62
63   STATE_SELECTTION,
64   STATE_PATTERN,
65   STATE_ATOM,
66   STATE_PATCH,
67   STATE_PRODUCT,
68
69   STATE_PEPOCH,
70   STATE_PVERSION,
71   STATE_PRELEASE,
72   STATE_PARCH,
73
74   NUMSTATES
75 };
76
77 struct stateswitch {
78   enum state from;
79   char *ename;
80   enum state to;
81   int docontent;
82 };
83
84 static struct stateswitch stateswitches[] = {
85   { STATE_START,       "channel",         STATE_CHANNEL, 0 },
86   { STATE_CHANNEL,     "subchannel",      STATE_SUBCHANNEL, 0 },
87   { STATE_SUBCHANNEL,  "package",         STATE_PACKAGE, 0 },
88   { STATE_SUBCHANNEL,  "selection",       STATE_PACKAGE, 0 },
89   { STATE_SUBCHANNEL,  "pattern",         STATE_PACKAGE, 0 },
90   { STATE_SUBCHANNEL,  "atom",            STATE_PACKAGE, 0 },
91   { STATE_SUBCHANNEL,  "patch",           STATE_PACKAGE, 0 },
92   { STATE_SUBCHANNEL,  "product",         STATE_PACKAGE, 0 },
93   { STATE_PACKAGE,     "name",            STATE_NAME, 1 },
94   { STATE_PACKAGE,     "vendor",          STATE_VENDOR, 1 },
95   { STATE_PACKAGE,     "buildtime",       STATE_BUILDTIME, 1 },
96   { STATE_PACKAGE,     "epoch",           STATE_PEPOCH, 1 },
97   { STATE_PACKAGE,     "version",         STATE_PVERSION, 1 },
98   { STATE_PACKAGE,     "release",         STATE_PRELEASE, 1 },
99   { STATE_PACKAGE,     "arch",            STATE_PARCH, 1 },
100   { STATE_PACKAGE,     "history",         STATE_HISTORY, 0 },
101   { STATE_PACKAGE,     "provides",        STATE_PROVIDES, 0 },
102   { STATE_PACKAGE,     "requires",        STATE_REQUIRES, 0 },
103   { STATE_PACKAGE,     "obsoletes",       STATE_OBSOLETES , 0 },
104   { STATE_PACKAGE,     "conflicts",       STATE_CONFLICTS , 0 },
105   { STATE_PACKAGE,     "recommends" ,     STATE_RECOMMENDS , 0 },
106   { STATE_PACKAGE,     "supplements",     STATE_SUPPLEMENTS, 0 },
107   { STATE_PACKAGE,     "suggests",        STATE_SUGGESTS, 0 },
108   { STATE_PACKAGE,     "enhances",        STATE_ENHANCES, 0 },
109   { STATE_PACKAGE,     "freshens",        STATE_FRESHENS, 0 },
110
111   { STATE_HISTORY,     "update",          STATE_UPDATE, 0 },
112   { STATE_UPDATE,      "epoch",           STATE_EPOCH, 1 },
113   { STATE_UPDATE,      "version",         STATE_VERSION, 1 },
114   { STATE_UPDATE,      "release",         STATE_RELEASE, 1 },
115   { STATE_UPDATE,      "arch",            STATE_ARCH, 1 },
116
117   { STATE_PROVIDES,    "dep",             STATE_PROVIDESENTRY, 0 },
118   { STATE_REQUIRES,    "dep",             STATE_REQUIRESENTRY, 0 },
119   { STATE_OBSOLETES,   "dep",             STATE_OBSOLETESENTRY, 0 },
120   { STATE_CONFLICTS,   "dep",             STATE_CONFLICTSENTRY, 0 },
121   { STATE_RECOMMENDS,  "dep",             STATE_RECOMMENDSENTRY, 0 },
122   { STATE_SUPPLEMENTS, "dep",             STATE_SUPPLEMENTSENTRY, 0 },
123   { STATE_SUGGESTS,    "dep",             STATE_SUGGESTSENTRY, 0 },
124   { STATE_ENHANCES,    "dep",             STATE_ENHANCESENTRY, 0 },
125   { STATE_FRESHENS,    "dep",             STATE_FRESHENSENTRY, 0 },
126   { NUMSTATES }
127
128 };
129
130 /*
131  * parser data
132  */
133
134 typedef struct _parsedata {
135   // XML parser data
136   int depth;
137   enum state state;     // current state
138   int statedepth;
139   char *content;        // buffer for content of node
140   int lcontent;         // actual length of current content
141   int acontent;         // actual buffer size
142   int docontent;        // handle content
143
144   // repo data
145   Pool *pool;           // current pool
146   Repo *repo;           // current repo
147   Repodata *data;       // current repo data
148   Solvable *solvable;   // current solvable
149   Offset freshens;      // current freshens vector
150
151   // package data
152   int  epoch;           // epoch (as offset into evrspace)
153   int  version;         // version (as offset into evrspace)
154   int  release;         // release (as offset into evrspace)
155   char *evrspace;       // buffer for evr
156   int  aevrspace;       // actual buffer space
157   int  levrspace;       // actual evr length
158   char *kind;
159
160   struct stateswitch *swtab[NUMSTATES];
161   enum state sbtab[NUMSTATES];
162 } Parsedata;
163
164
165 /*------------------------------------------------------------------*/
166 /* E:V-R handling */
167
168 // create Id from epoch:version-release
169
170 static Id
171 evr2id(Pool *pool, Parsedata *pd, const char *e, const char *v, const char *r)
172 {
173   char *c;
174   int l;
175
176   // treat explitcit 0 as NULL
177   if (e && !strcmp(e, "0"))
178     e = NULL;
179
180   if (v && !e)
181     {
182       const char *v2;
183       // scan version for ":"
184       for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++)      // skip leading digits
185         ;
186       // if version contains ":", set epoch to "0"
187       if (v2 > v && *v2 == ':')
188         e = "0";
189     }
190   
191   // compute length of Id string
192   l = 1;  // for the \0
193   if (e)
194     l += strlen(e) + 1;  // e:
195   if (v)
196     l += strlen(v);      // v
197   if (r)
198     l += strlen(r) + 1;  // -r
199
200   // extend content if not sufficient
201   if (l > pd->acontent)
202     {
203       pd->content = (char *)realloc(pd->content, l + 256);
204       pd->acontent = l + 256;
205     }
206
207   // copy e-v-r to content
208   c = pd->content;
209   if (e)
210     {
211       strcpy(c, e);
212       c += strlen(c);
213       *c++ = ':';
214     }
215   if (v)
216     {
217       strcpy(c, v);
218       c += strlen(c);
219     }
220   if (r)
221     {
222       *c++ = '-';
223       strcpy(c, r);
224       c += strlen(c);
225     }
226   *c = 0;
227   // if nothing inserted, return Id 0
228   if (!*pd->content)
229     return ID_NULL;
230 #if 0
231   fprintf(stderr, "evr: %s\n", pd->content);
232 #endif
233   // intern and create
234   return str2id(pool, pd->content, 1);
235 }
236
237
238 // create e:v-r from attributes
239 // atts is array of name,value pairs, NULL at end
240 //   even index into atts is name
241 //   odd index is value
242 //
243 static Id
244 evr_atts2id(Pool *pool, Parsedata *pd, const char **atts)
245 {
246   const char *e, *v, *r;
247   e = v = r = 0;
248   for (; *atts; atts += 2)
249     {
250       if (!strcmp(*atts, "epoch"))
251         e = atts[1];
252       else if (!strcmp(*atts, "version"))
253         v = atts[1];
254       else if (!strcmp(*atts, "release"))
255         r = atts[1];
256     }
257   return evr2id(pool, pd, e, v, r);
258 }
259
260 /*------------------------------------------------------------------*/
261 /* rel operator handling */
262
263 struct flagtab {
264   char *from;
265   int to;
266 };
267
268 static struct flagtab flagtab[] = {
269   { ">",  REL_GT },
270   { "=",  REL_EQ },
271   { ">=", REL_GT|REL_EQ },
272   { "<",  REL_LT },
273   { "!=", REL_GT|REL_LT },
274   { "<=", REL_LT|REL_EQ },
275   { "(any)", REL_LT|REL_EQ|REL_GT },
276   { "==", REL_EQ },
277   { "gt", REL_GT },
278   { "eq", REL_EQ },
279   { "ge", REL_GT|REL_EQ },
280   { "lt", REL_LT },
281   { "ne", REL_GT|REL_LT },
282   { "le", REL_LT|REL_EQ },
283   { "gte", REL_GT|REL_EQ },
284   { "lte", REL_LT|REL_EQ },
285   { "GT", REL_GT },
286   { "EQ", REL_EQ },
287   { "GE", REL_GT|REL_EQ },
288   { "LT", REL_LT },
289   { "NE", REL_GT|REL_LT },
290   { "LE", REL_LT|REL_EQ }
291 };
292
293 /*
294  * process new dependency from parser
295  *  olddeps = already collected deps, this defines the 'kind' of dep
296  *  atts = array of name,value attributes of dep
297  *  isreq == 1 if its a requires
298  */
299
300 static unsigned int
301 adddep(Pool *pool, Parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
302 {
303   Id id, name, marker;
304   const char *n, *f, *k;
305   const char **a;
306
307   n = f = k = NULL;
308   marker = isreq ? -SOLVABLE_PREREQMARKER : 0;
309
310   /* loop over name,value pairs */
311   for (a = atts; *a; a += 2)
312     {
313       if (!strcmp(*a, "name"))
314         n = a[1];
315       if (!strcmp(*a, "kind"))
316         k = a[1];
317       else if (!strcmp(*a, "op"))
318         f = a[1];
319       else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
320         marker = SOLVABLE_PREREQMARKER;
321     }
322   if (!n)                              /* quit if no name found */
323     return olddeps;
324
325   /* kind, name */
326   if (k && !strcmp(k, "package"))
327     k = NULL;                          /* package is default */
328
329   if (k)                               /* if kind!=package, intern <kind>:<name> */
330     {
331       int l = strlen(k) + 1 + strlen(n) + 1;
332       if (l > pd->acontent)            /* extend buffer if needed */
333         {
334           pd->content = (char *)realloc(pd->content, l + 256);
335           pd->acontent = l + 256;
336         }
337       sprintf(pd->content, "%s:%s", k, n);
338       name = str2id(pool, pd->content, 1);
339     }
340   else
341     {
342       name = str2id(pool, n, 1);       /* package: just intern <name> */
343     }
344
345   if (f)                               /* operator ? */
346     {
347       /* intern e:v-r */
348       Id evr = evr_atts2id(pool, pd, atts);
349       /* parser operator to flags */
350       int flags;
351       for (flags = 0; flags < sizeof(flagtab)/sizeof(*flagtab); flags++)
352         if (!strcmp(f, flagtab[flags].from))
353           {
354             flags = flagtab[flags].to;
355             break;
356           }
357       if (flags > 7)
358         flags = 0;
359       /* intern rel */
360       id = rel2id(pool, name, evr, flags, 1);
361     }
362   else
363     id = name;                         /* no operator */
364
365   /* add new dependency to repo */
366   return repo_addid_dep(pd->repo, olddeps, id, marker);
367 }
368
369
370 /*----------------------------------------------------------------*/
371
372 /*
373  * XML callback
374  * <name>
375  * 
376  */
377
378 static void XMLCALL
379 startElement(void *userData, const char *name, const char **atts)
380 {
381   Parsedata *pd = (Parsedata *)userData;
382   struct stateswitch *sw;
383   Pool *pool = pd->pool;
384   Solvable *s = pd->solvable;
385
386   if (pd->depth != pd->statedepth)
387     {
388       pd->depth++;
389       return;
390     }
391
392   /* ignore deps element */
393   if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
394     return;
395
396   pd->depth++;
397
398   /* find node name in stateswitch */
399   if (!pd->swtab[pd->state])
400     return;
401   for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
402   {
403     if (!strcmp(sw->ename, name))
404       break;
405   }
406
407   /* check if we're at the right level */
408   if (sw->from != pd->state)
409     {
410 #if 0
411       fprintf(stderr, "into unknown: %s\n", name);
412 #endif
413       return;
414     }
415   
416   // set new state
417   pd->state = sw->to;
418
419   pd->docontent = sw->docontent;
420   pd->statedepth = pd->depth;
421
422   // start with empty content
423   // (will collect data until end element
424   pd->lcontent = 0;
425   *pd->content = 0;
426
427   switch (pd->state)
428     {
429
430     case STATE_NAME:
431       if (pd->kind)                    /* if kind is set (non package) */
432         {
433           strcpy(pd->content, pd->kind);
434           pd->lcontent = strlen(pd->content);
435           pd->content[pd->lcontent++] = ':';   /* prefix name with '<kind>:' */
436           pd->content[pd->lcontent] = 0;
437         }
438       break;
439
440     case STATE_PACKAGE:                /* solvable name */
441       pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
442       if (!strcmp(name, "selection"))
443         pd->kind = "selection";
444       else if (!strcmp(name, "pattern"))
445         pd->kind = "pattern";
446       else if (!strcmp(name, "atom"))
447         pd->kind = "atom";
448       else if (!strcmp(name, "product"))
449         pd->kind = "product";
450       else if (!strcmp(name, "patch"))
451         pd->kind = "patch";
452       else
453         pd->kind = NULL;               /* default is package */
454       pd->levrspace = 1;
455       pd->epoch = 0;
456       pd->version = 0;
457       pd->release = 0;
458       pd->freshens = 0;
459 #if 0
460       fprintf(stderr, "package #%d\n", s - pool->solvables);
461 #endif
462       break;
463
464     case STATE_UPDATE:
465       pd->levrspace = 1;
466       pd->epoch = 0;
467       pd->version = 0;
468       pd->release = 0;
469       break;
470
471     case STATE_PROVIDES:               /* start of provides */
472       s->provides = 0;
473       break;
474     case STATE_PROVIDESENTRY:          /* entry within provides */
475       s->provides = adddep(pool, pd, s->provides, atts, 0);
476       break;
477     case STATE_REQUIRES:
478       s->requires = 0;
479       break;
480     case STATE_REQUIRESENTRY:
481       s->requires = adddep(pool, pd, s->requires, atts, 1);
482       break;
483     case STATE_OBSOLETES:
484       s->obsoletes = 0;
485       break;
486     case STATE_OBSOLETESENTRY:
487       s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
488       break;
489     case STATE_CONFLICTS:
490       s->conflicts = 0;
491       break;
492     case STATE_CONFLICTSENTRY:
493       s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
494       break;
495     case STATE_RECOMMENDS:
496       s->recommends = 0;
497       break;
498     case STATE_RECOMMENDSENTRY:
499       s->recommends = adddep(pool, pd, s->recommends, atts, 0);
500       break;
501     case STATE_SUPPLEMENTS:
502       s->supplements= 0;
503       break;
504     case STATE_SUPPLEMENTSENTRY:
505       s->supplements = adddep(pool, pd, s->supplements, atts, 0);
506       break;
507     case STATE_SUGGESTS:
508       s->suggests = 0;
509       break;
510     case STATE_SUGGESTSENTRY:
511       s->suggests = adddep(pool, pd, s->suggests, atts, 0);
512       break;
513     case STATE_ENHANCES:
514       s->enhances = 0;
515       break;
516     case STATE_ENHANCESENTRY:
517       s->enhances = adddep(pool, pd, s->enhances, atts, 0);
518       break;
519     case STATE_FRESHENS:
520       pd->freshens = 0;
521       break;
522     case STATE_FRESHENSENTRY:
523       pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
524       break;
525     default:
526       break;
527     }
528 }
529
530 static const char* findKernelFlavor(Parsedata *pd, Solvable *s)
531 {
532   Pool *pool = pd->pool;
533
534   Id pid, *pidp = 0;
535   
536   if (s->provides)
537     {
538       for (pidp = pd->repo->idarraydata + s->provides; pidp && (pid = *pidp++) != 0; )
539         {
540           Reldep *prd;
541           const char *depname;
542           
543           if (!ISRELDEP(pid))
544             continue;               /* wrong provides name */
545           prd = GETRELDEP(pool, pid);
546           depname = id2str(pool, prd->name);
547           if (!strncmp(depname, "kernel-", strlen("kernel-")))
548             {
549               return depname + strlen("kernel-");
550             }
551         }
552     }
553
554   if (!s->requires)
555     return 0;
556
557   for (pidp = pd->repo->idarraydata + s->requires ; pidp && (pid = *pidp++) != 0; )
558     {
559       const char *depname = 0;
560
561       if (!ISRELDEP(pid))
562         {
563           depname = id2str(pool, pid);
564         } 
565       else 
566         {
567           Reldep *prd = GETRELDEP(pool, pid);
568           depname = id2str(pool, prd->name);
569         }
570       if (!strncmp(depname, "kernel-", strlen("kernel-")))
571         {
572           return depname + strlen("kernel-");
573         }
574     }
575
576   return 0;
577 }
578
579
580 /*
581  * XML callback
582  * </name>
583  * 
584  * create Solvable from collected data
585  */
586
587 static void XMLCALL
588 endElement(void *userData, const char *name)
589 {
590   Parsedata *pd = (Parsedata *)userData;
591   Pool *pool = pd->pool;
592   Solvable *s = pd->solvable;
593   Id evr;
594   unsigned int t=0;
595
596   if (pd->depth != pd->statedepth)
597     {
598       pd->depth--;
599       // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
600       return;
601     }
602
603   /* ignore deps element */
604   if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
605     return;
606
607   pd->depth--;
608   pd->statedepth--;
609   switch (pd->state)
610     {
611
612     case STATE_PACKAGE:                /* package complete */
613       if (!s->arch)                    /* default to "noarch" */
614         s->arch = ARCH_NOARCH;
615
616       if (!s->evr && pd->version)      /* set solvable evr */
617         s->evr = evr2id(pool, pd,
618                         pd->epoch   ? pd->evrspace + pd->epoch   : 0,
619                         pd->version ? pd->evrspace + pd->version : 0,
620                         pd->release ? pd->evrspace + pd->release : 0);
621       /* ensure self-provides */
622       if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
623         s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
624       s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, pd->freshens);
625       s->conflicts = repo_fix_conflicts(pd->repo, s->conflicts);
626       pd->freshens = 0;
627
628       /* see bugzilla bnc#190163 */
629       const char *flavor = findKernelFlavor(pd, s);
630       if (flavor) 
631         {
632           char *cflavor = strdup(flavor);       /* make pointer safe */
633
634           Id npr;
635           Id pid;
636
637           /* this is either a kernel package or a kmp */
638           if (s->provides)
639             {
640               Offset prov = s->provides;
641               npr = 0;
642               while ((pid = pd->repo->idarraydata[prov++]) != 0)
643                 {
644                   const char *depname = 0;
645                   Reldep *prd = 0;
646
647                   if (ISRELDEP(pid))
648                     {
649                       prd = GETRELDEP(pool, pid);
650                       depname = id2str(pool, prd->name);
651                     }
652                   else
653                     {
654                       depname = id2str(pool, pid);
655                     }
656
657
658                   if (!strncmp(depname, "kernel(", strlen("kernel(")) && !strchr(depname, ':'))
659                     {
660                       char newdep[100];
661                       strcpy(newdep, "kernel(");
662                       strncat(newdep, cflavor, sizeof(newdep) - 1);
663                       strncat(newdep, ":", sizeof(newdep) - 1);
664                       strncat(newdep, depname + strlen("kernel("), sizeof(newdep) - 1);
665                       pid = str2id(pool, newdep, 1);
666                       if (prd)
667                         pid = rel2id(pool, pid, prd->evr, prd->flags, 1);
668                     }
669
670                   npr = repo_addid_dep(pd->repo, npr, pid, 0);
671                 }
672               s->provides = npr;
673             }
674 #if 1
675
676           if (s->requires)
677             {
678               Offset reqs = s->requires;
679               npr = 0;
680               while ((pid = pd->repo->idarraydata[reqs++]) != 0)
681                 {
682                   const char *depname = 0;
683                   Reldep *prd = 0;
684
685                   if (ISRELDEP(pid))
686                     {
687                       prd = GETRELDEP(pool, pid);
688                       depname = id2str(pool, prd->name);
689                     }
690                   else
691                     {
692                       depname = id2str(pool, pid);
693                     }
694
695                   if (!strncmp(depname, "kernel(", strlen("kernel(")) && !strchr(depname, ':'))
696                     {
697                       char newdep[100];
698                       strcpy(newdep, "kernel(");
699                       strncat(newdep, cflavor, sizeof(newdep) - 1);
700                       strncat(newdep, ":", sizeof(newdep) - 1);
701                       strncat(newdep, depname + strlen("kernel("), sizeof(newdep) - 1);
702                       pid = str2id(pool, newdep, 1);
703                       if (prd)
704                         pid = rel2id(pool, pid, prd->evr, prd->flags, 1);
705                     }
706                   npr = repo_addid_dep(pd->repo, npr, pid, 0);
707                 }
708               s->requires = npr;
709             }
710 #endif
711           free(cflavor);
712         }
713       break;
714     case STATE_NAME:
715       s->name = str2id(pool, pd->content, 1);
716       break;
717     case STATE_VENDOR:
718       s->vendor = str2id(pool, pd->content, 1);
719       break;
720     case STATE_BUILDTIME:
721       t = atoi (pd->content);
722       if (t)
723         repodata_set_num(pd->data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
724       break;    
725     case STATE_UPDATE:                 /* new version, keeping all other metadata */
726       evr = evr2id(pool, pd,
727                    pd->epoch   ? pd->evrspace + pd->epoch   : 0,
728                    pd->version ? pd->evrspace + pd->version : 0,
729                    pd->release ? pd->evrspace + pd->release : 0);
730       pd->levrspace = 1;
731       pd->epoch = 0;
732       pd->version = 0;
733       pd->release = 0;
734       /* use highest evr */
735       if (!s->evr || evrcmp(pool, s->evr, evr, EVRCMP_MATCH_RELEASE) <= 0)
736         s->evr = evr;
737       break;
738     case STATE_EPOCH:
739     case STATE_VERSION:
740     case STATE_RELEASE:
741     case STATE_PEPOCH:
742     case STATE_PVERSION:
743     case STATE_PRELEASE:
744       /* ensure buffer space */
745       if (pd->lcontent + 1 + pd->levrspace > pd->aevrspace)
746         {
747           pd->evrspace = (char *)realloc(pd->evrspace, pd->lcontent + 1 + pd->levrspace + 256);
748           pd->aevrspace = pd->lcontent + 1 + pd->levrspace + 256;
749         }
750       memcpy(pd->evrspace + pd->levrspace, pd->content, pd->lcontent + 1);
751       if (pd->state == STATE_EPOCH || pd->state == STATE_PEPOCH)
752         pd->epoch = pd->levrspace;
753       else if (pd->state == STATE_VERSION || pd->state == STATE_PVERSION)
754         pd->version = pd->levrspace;
755       else
756         pd->release = pd->levrspace;
757       pd->levrspace += pd->lcontent + 1;
758       break;
759     case STATE_ARCH:
760     case STATE_PARCH:
761       s->arch = str2id(pool, pd->content, 1);
762       break;
763     default:
764       break;
765     }
766   pd->state = pd->sbtab[pd->state];
767   pd->docontent = 0;
768   // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
769 }
770
771
772 /*
773  * XML callback
774  * character data
775  * 
776  */
777
778 static void XMLCALL
779 characterData(void *userData, const XML_Char *s, int len)
780 {
781   Parsedata *pd = (Parsedata *)userData;
782   int l;
783   char *c;
784
785   // check if current nodes content is interesting
786   if (!pd->docontent)
787     return;
788
789   // adapt content buffer
790   l = pd->lcontent + len + 1;
791   if (l > pd->acontent)
792     {
793       pd->content = (char *)realloc(pd->content, l + 256);
794       pd->acontent = l + 256;
795     }
796   // append new content to buffer
797   c = pd->content + pd->lcontent;
798   pd->lcontent += len;
799   while (len-- > 0)
800     *c++ = *s++;
801   *c = 0;
802 }
803
804 /*-------------------------------------------------------------------*/
805
806 #define BUFF_SIZE 8192
807
808 /*
809  * read 'helix' type xml from fp
810  * add packages to pool/repo
811  * 
812  */
813
814 void
815 repo_add_helix(Repo *repo, FILE *fp, int flags)
816 {
817   Pool *pool = repo->pool;
818   Parsedata pd;
819   Repodata *data;
820   char buf[BUFF_SIZE];
821   int i, l;
822   struct stateswitch *sw;
823
824   if (!(flags & REPO_REUSE_REPODATA))
825     data = repo_add_repodata(repo, 0);
826   else
827     data = repo_last_repodata(repo);
828   
829   /* prepare parsedata */
830   memset(&pd, 0, sizeof(pd));
831   for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
832     {
833       if (!pd.swtab[sw->from])
834         pd.swtab[sw->from] = sw;
835       pd.sbtab[sw->to] = sw->from;
836     }
837
838   pd.pool = pool;
839   pd.repo = repo;
840
841   pd.content = (char *)malloc(256);     /* must hold all solvable kinds! */
842   pd.acontent = 256;
843   pd.lcontent = 0;
844
845   pd.evrspace = (char *)malloc(256);
846   pd.aevrspace= 256;
847   pd.levrspace = 1;
848   pd.data = data;
849
850   // set up XML parser
851
852   XML_Parser parser = XML_ParserCreate(NULL);
853   XML_SetUserData(parser, &pd);       /* make parserdata available to XML callbacks */
854   XML_SetElementHandler(parser, startElement, endElement);
855   XML_SetCharacterDataHandler(parser, characterData);
856
857   // read/parse XML file
858   for (;;)
859     {
860       l = fread(buf, 1, sizeof(buf), fp);
861       if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
862         {
863           pool_debug(pool, SAT_FATAL, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
864           exit(1);
865         }
866       if (l == 0)
867         break;
868     }
869   XML_ParserFree(parser);
870   free(pd.content);
871   free(pd.evrspace);
872
873   if (!(flags & REPO_NO_INTERNALIZE))
874     repodata_internalize(data);
875 }