4 * Parse 'helix' XML representation
17 #include "source_helix.h"
21 /* XML parser states */
44 STATE_RECOMMENDSENTRY,
46 STATE_SUPPLEMENTSENTRY,
68 #define PACK_BLOCK 255
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 },
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 },
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 },
121 // Deps are stored as offsets into source->idarraydata
122 typedef struct _deps {
138 typedef struct _parsedata {
141 enum state state; // current state
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
149 int pack; // number of solvables
151 Pool *pool; // current pool
152 Source *source; // current source
153 Solvable *start; // collected solvables
156 Deps *deps; // dependencies array, indexed by pack#
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
167 struct stateswitch *swtab[NUMSTATES];
168 enum state sbtab[NUMSTATES];
172 /*------------------------------------------------------------------*/
175 // create Id from epoch:version-release
178 evr2id(Pool *pool, Parsedata *pd, const char *e, const char *v, const char *r)
183 // treat explitcit 0 as NULL
184 if (e && !strcmp(e, "0"))
190 // scan version for ":"
191 for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++) // skip leading digits
193 // if version contains ":", set epoch to "0"
194 if (v2 > v && *v2 == ':')
198 // compute length of Id string
201 l += strlen(e) + 1; // e:
205 l += strlen(r) + 1; // -r
207 // extend content if not sufficient
208 if (l > pd->acontent)
210 pd->content = (char *)realloc(pd->content, l + 256);
211 pd->acontent = l + 256;
214 // copy e-v-r to content
234 // if nothing inserted, return Id 0
238 fprintf(stderr, "evr: %s\n", pd->content);
241 return str2id(pool, pd->content, 1);
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
251 evr_atts2id(Pool *pool, Parsedata *pd, const char **atts)
253 const char *e, *v, *r;
255 for (; *atts; atts += 2)
257 if (!strcmp(*atts, "epoch"))
259 else if (!strcmp(*atts, "version"))
261 else if (!strcmp(*atts, "release"))
264 return evr2id(pool, pd, e, v, r);
267 /*------------------------------------------------------------------*/
268 /* rel operator handling */
275 static struct flagtab flagtab[] = {
278 { ">=", REL_GT|REL_EQ },
280 { "!=", REL_GT|REL_LT },
281 { "<=", REL_LT|REL_EQ },
282 { "(any)", REL_LT|REL_EQ|REL_GT },
286 { "ge", REL_GT|REL_EQ },
288 { "ne", REL_GT|REL_LT },
289 { "le", REL_LT|REL_EQ },
290 { "gte", REL_GT|REL_EQ },
291 { "lte", REL_LT|REL_EQ },
294 { "GE", REL_GT|REL_EQ },
296 { "NE", REL_GT|REL_LT },
297 { "LE", REL_LT|REL_EQ }
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
308 adddep(Pool *pool, Parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
311 const char *n, *f, *k;
316 /* loop over name,value pairs */
317 for (a = atts; *a; a += 2)
319 if (!strcmp(*a, "name"))
321 if (!strcmp(*a, "kind"))
323 else if (!strcmp(*a, "op"))
325 else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
328 if (!n) /* quit if no name found */
332 if (k && !strcmp(k, "package"))
333 k = NULL; /* package is default */
335 if (k) /* if kind!=package, intern <kind>:<name> */
337 int l = strlen(k) + 1 + strlen(n) + 1;
338 if (l > pd->acontent) /* extend buffer if needed */
340 pd->content = (char *)realloc(pd->content, l + 256);
341 pd->acontent = l + 256;
343 sprintf(pd->content, "%s:%s", k, n);
344 name = str2id(pool, pd->content, 1);
347 name = str2id(pool, n, 1); /* package: just intern <name> */
349 if (f) /* operator ? */
352 Id evr = evr_atts2id(pool, pd, atts);
353 /* parser operator to flags */
355 for (flags = 0; flags < sizeof(flagtab)/sizeof(*flagtab); flags++)
356 if (!strcmp(f, flagtab[flags].from))
358 flags = flagtab[flags].to;
364 id = rel2id(pool, name, evr, flags, 1);
367 id = name; /* no operator */
369 /* add new dependency to source */
370 return source_addid_dep(pd->source, olddeps, id, isreq);
374 /*----------------------------------------------------------------*/
383 startElement(void *userData, const char *name, const char **atts)
385 Parsedata *pd = (Parsedata *)userData;
386 struct stateswitch *sw;
387 Pool *pool = pd->pool;
389 if (pd->depth != pd->statedepth)
395 /* ignore deps element */
396 if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
401 /* find node name in stateswitch */
402 for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
404 if (!strcmp(sw->ename, name))
408 /* check if we're at the right level */
409 if (sw->from != pd->state)
412 fprintf(stderr, "into unknown: %s\n", name);
420 pd->docontent = sw->docontent;
421 pd->statedepth = pd->depth;
423 // start with empty content
424 // (will collect data until end element
432 if (pd->kind) /* if kind is set (non package) */
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;
441 case STATE_SUBCHANNEL:
446 case STATE_PACKAGE: /* solvable name */
448 if ((pd->pack & PACK_BLOCK) == 0) /* alloc new block ? */
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));
454 pd->deps = (Deps *)malloc((pd->pack + PACK_BLOCK + 1) * sizeof(Deps));
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));
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"))
466 else if (!strcmp(name, "product"))
467 pd->kind = "product";
468 else if (!strcmp(name, "patch"))
471 pd->kind = NULL; /* default is package */
477 fprintf(stderr, "package #%d\n", pd->pack);
488 case STATE_PROVIDES: /* start of provides */
489 pd->deps[pd->pack].provides = 0;
491 case STATE_PROVIDESENTRY: /* entry within provides */
492 pd->deps[pd->pack].provides = adddep(pool, pd, pd->deps[pd->pack].provides, atts, 0);
495 pd->deps[pd->pack].requires = 0;
497 case STATE_REQUIRESENTRY:
498 pd->deps[pd->pack].requires = adddep(pool, pd, pd->deps[pd->pack].requires, atts, 1);
500 case STATE_OBSOLETES:
501 pd->deps[pd->pack].obsoletes = 0;
503 case STATE_OBSOLETESENTRY:
504 pd->deps[pd->pack].obsoletes = adddep(pool, pd, pd->deps[pd->pack].obsoletes, atts, 0);
506 case STATE_CONFLICTS:
507 pd->deps[pd->pack].conflicts = 0;
509 case STATE_CONFLICTSENTRY:
510 pd->deps[pd->pack].conflicts = adddep(pool, pd, pd->deps[pd->pack].conflicts, atts, 0);
512 case STATE_RECOMMENDS:
513 pd->deps[pd->pack].recommends = 0;
515 case STATE_RECOMMENDSENTRY:
516 pd->deps[pd->pack].recommends = adddep(pool, pd, pd->deps[pd->pack].recommends, atts, 0);
518 case STATE_SUPPLEMENTS:
519 pd->deps[pd->pack].supplements= 0;
521 case STATE_SUPPLEMENTSENTRY:
522 pd->deps[pd->pack].supplements = adddep(pool, pd, pd->deps[pd->pack].supplements, atts, 0);
525 pd->deps[pd->pack].suggests = 0;
527 case STATE_SUGGESTSENTRY:
528 pd->deps[pd->pack].suggests = adddep(pool, pd, pd->deps[pd->pack].suggests, atts, 0);
531 pd->deps[pd->pack].enhances = 0;
533 case STATE_ENHANCESENTRY:
534 pd->deps[pd->pack].enhances = adddep(pool, pd, pd->deps[pd->pack].enhances, atts, 0);
537 pd->deps[pd->pack].freshens = 0;
539 case STATE_FRESHENSENTRY:
540 pd->deps[pd->pack].freshens = adddep(pool, pd, pd->deps[pd->pack].freshens, atts, 0);
552 * create Solvable from collected data
556 endElement(void *userData, const char *name)
558 Parsedata *pd = (Parsedata *)userData;
559 Pool *pool = pd->pool;
560 Solvable *s = pd->start ? pd->start + pd->pack : NULL;
563 if (pd->depth != pd->statedepth)
566 // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
570 /* ignore deps element */
571 if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
579 case STATE_PACKAGE: /* package complete */
581 if (!s->arch) /* default to "noarch" */
582 s->arch = ARCH_NOARCH;
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 */
596 s->name = str2id(pool, pd->content, 1);
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);
607 /* use highest evr */
608 if (!s->evr || evrcmp(pool, s->evr, evr) <= 0)
617 /* ensure buffer space */
618 if (pd->lcontent + 1 + pd->levrspace > pd->aevrspace)
620 pd->evrspace = (char *)realloc(pd->evrspace, pd->lcontent + 1 + pd->levrspace + 256);
621 pd->aevrspace = pd->lcontent + 1 + pd->levrspace + 256;
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;
629 pd->release = pd->levrspace;
630 pd->levrspace += pd->lcontent + 1;
634 s->arch = str2id(pool, pd->content, 1);
639 pd->state = pd->sbtab[pd->state];
641 // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
652 characterData(void *userData, const XML_Char *s, int len)
654 Parsedata *pd = (Parsedata *)userData;
658 // check if current nodes content is interesting
662 // adapt content buffer
663 l = pd->lcontent + len + 1;
664 if (l > pd->acontent)
666 pd->content = (char *)realloc(pd->content, l + 256);
667 pd->acontent = l + 256;
669 // append new content to buffer
670 c = pd->content + pd->lcontent;
677 /*-------------------------------------------------------------------*/
679 #define BUFF_SIZE 8192
682 * read 'helix' type xml from fp
683 * add packages to pool/source
688 pool_addsource_helix(Pool *pool, FILE *fp)
696 struct stateswitch *sw;
698 // create empty source
699 source = pool_addsource_empty(pool);
702 memset(&pd, 0, sizeof(pd));
703 for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
705 if (!pd.swtab[sw->from])
706 pd.swtab[sw->from] = sw;
707 pd.sbtab[sw->to] = sw->from;
713 pd.content = (char *)malloc(256); /* must hold all solvable kinds! */
717 pd.evrspace = (char *)malloc(256);
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);
728 // read/parse XML file
731 l = fread(buf, 1, sizeof(buf), fp);
732 if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
734 fprintf(stderr, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
740 XML_ParserFree(parser);
742 // adapt package count
743 pool->nsolvables += pd.pack;
744 source->nsolvables = pd.pack;
746 // now set dependency pointers for each solvable
748 solvable = pool->solvables + source->start;
749 for (i = 0; i < pd.pack; i++, solvable++)
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;