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