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