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