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