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