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