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