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