1 /* -*- mode: C; c-file-style: "gnu"; fill-column: 78 -*- */
5 * Parse 'helix' XML representation
10 #include <sys/types.h>
18 #include "repo_helix.h"
22 /* XML parser states */
46 STATE_RECOMMENDSENTRY,
48 STATE_SUPPLEMENTSENTRY,
70 #define PACK_BLOCK 255
79 static struct stateswitch stateswitches[] = {
80 { STATE_START, "channel", STATE_CHANNEL, 0 },
81 { STATE_CHANNEL, "subchannel", STATE_SUBCHANNEL, 0 },
82 { STATE_SUBCHANNEL, "package", STATE_PACKAGE, 0 },
83 { STATE_SUBCHANNEL, "selection", STATE_PACKAGE, 0 },
84 { STATE_SUBCHANNEL, "pattern", STATE_PACKAGE, 0 },
85 { STATE_SUBCHANNEL, "atom", STATE_PACKAGE, 0 },
86 { STATE_SUBCHANNEL, "patch", STATE_PACKAGE, 0 },
87 { STATE_SUBCHANNEL, "product", STATE_PACKAGE, 0 },
88 { STATE_PACKAGE, "name", STATE_NAME, 1 },
89 { STATE_PACKAGE, "vendor", STATE_VENDOR, 1 },
90 { STATE_PACKAGE, "epoch", STATE_PEPOCH, 1 },
91 { STATE_PACKAGE, "version", STATE_PVERSION, 1 },
92 { STATE_PACKAGE, "release", STATE_PRELEASE, 1 },
93 { STATE_PACKAGE, "arch", STATE_PARCH, 1 },
94 { STATE_PACKAGE, "history", STATE_HISTORY, 0 },
95 { STATE_PACKAGE, "provides", STATE_PROVIDES, 0 },
96 { STATE_PACKAGE, "requires", STATE_REQUIRES, 0 },
97 { STATE_PACKAGE, "obsoletes", STATE_OBSOLETES , 0 },
98 { STATE_PACKAGE, "conflicts", STATE_CONFLICTS , 0 },
99 { STATE_PACKAGE, "recommends" , STATE_RECOMMENDS , 0 },
100 { STATE_PACKAGE, "supplements", STATE_SUPPLEMENTS, 0 },
101 { STATE_PACKAGE, "suggests", STATE_SUGGESTS, 0 },
102 { STATE_PACKAGE, "enhances", STATE_ENHANCES, 0 },
103 { STATE_PACKAGE, "freshens", STATE_FRESHENS, 0 },
105 { STATE_HISTORY, "update", STATE_UPDATE, 0 },
106 { STATE_UPDATE, "epoch", STATE_EPOCH, 1 },
107 { STATE_UPDATE, "version", STATE_VERSION, 1 },
108 { STATE_UPDATE, "release", STATE_RELEASE, 1 },
109 { STATE_UPDATE, "arch", STATE_ARCH, 1 },
111 { STATE_PROVIDES, "dep", STATE_PROVIDESENTRY, 0 },
112 { STATE_REQUIRES, "dep", STATE_REQUIRESENTRY, 0 },
113 { STATE_OBSOLETES, "dep", STATE_OBSOLETESENTRY, 0 },
114 { STATE_CONFLICTS, "dep", STATE_CONFLICTSENTRY, 0 },
115 { STATE_RECOMMENDS, "dep", STATE_RECOMMENDSENTRY, 0 },
116 { STATE_SUPPLEMENTS, "dep", STATE_SUPPLEMENTSENTRY, 0 },
117 { STATE_SUGGESTS, "dep", STATE_SUGGESTSENTRY, 0 },
118 { STATE_ENHANCES, "dep", STATE_ENHANCESENTRY, 0 },
119 { STATE_FRESHENS, "dep", STATE_FRESHENSENTRY, 0 },
128 typedef struct _parsedata {
131 enum state state; // current state
133 char *content; // buffer for content of node
134 int lcontent; // actual length of current content
135 int acontent; // actual buffer size
136 int docontent; // handle content
139 int pack; // number of solvables
141 Pool *pool; // current pool
142 Repo *repo; // current repo
143 Solvable *start; // collected solvables
146 int epoch; // epoch (as offset into evrspace)
147 int version; // version (as offset into evrspace)
148 int release; // release (as offset into evrspace)
149 char *evrspace; // buffer for evr
150 int aevrspace; // actual buffer space
151 int levrspace; // actual evr length
154 struct stateswitch *swtab[NUMSTATES];
155 enum state sbtab[NUMSTATES];
159 /*------------------------------------------------------------------*/
162 // create Id from epoch:version-release
165 evr2id(Pool *pool, Parsedata *pd, const char *e, const char *v, const char *r)
170 // treat explitcit 0 as NULL
171 if (e && !strcmp(e, "0"))
177 // scan version for ":"
178 for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++) // skip leading digits
180 // if version contains ":", set epoch to "0"
181 if (v2 > v && *v2 == ':')
185 // compute length of Id string
188 l += strlen(e) + 1; // e:
192 l += strlen(r) + 1; // -r
194 // extend content if not sufficient
195 if (l > pd->acontent)
197 pd->content = (char *)realloc(pd->content, l + 256);
198 pd->acontent = l + 256;
201 // copy e-v-r to content
221 // if nothing inserted, return Id 0
225 fprintf(stderr, "evr: %s\n", pd->content);
228 return str2id(pool, pd->content, 1);
232 // create e:v-r from attributes
233 // atts is array of name,value pairs, NULL at end
234 // even index into atts is name
235 // odd index is value
238 evr_atts2id(Pool *pool, Parsedata *pd, const char **atts)
240 const char *e, *v, *r;
242 for (; *atts; atts += 2)
244 if (!strcmp(*atts, "epoch"))
246 else if (!strcmp(*atts, "version"))
248 else if (!strcmp(*atts, "release"))
251 return evr2id(pool, pd, e, v, r);
254 /*------------------------------------------------------------------*/
255 /* rel operator handling */
262 static struct flagtab flagtab[] = {
265 { ">=", REL_GT|REL_EQ },
267 { "!=", REL_GT|REL_LT },
268 { "<=", REL_LT|REL_EQ },
269 { "(any)", REL_LT|REL_EQ|REL_GT },
273 { "ge", REL_GT|REL_EQ },
275 { "ne", REL_GT|REL_LT },
276 { "le", REL_LT|REL_EQ },
277 { "gte", REL_GT|REL_EQ },
278 { "lte", REL_LT|REL_EQ },
281 { "GE", REL_GT|REL_EQ },
283 { "NE", REL_GT|REL_LT },
284 { "LE", REL_LT|REL_EQ }
288 * process new dependency from parser
289 * olddeps = already collected deps, this defines the 'kind' of dep
290 * atts = array of name,value attributes of dep
291 * isreq == 1 if its a requires
295 adddep(Pool *pool, Parsedata *pd, unsigned int olddeps, const char **atts, int isreq)
298 const char *n, *f, *k;
303 /* loop over name,value pairs */
304 for (a = atts; *a; a += 2)
306 if (!strcmp(*a, "name"))
308 if (!strcmp(*a, "kind"))
310 else if (!strcmp(*a, "op"))
312 else if (isreq && !strcmp(*a, "pre") && a[1][0] == '1')
315 if (!n) /* quit if no name found */
319 if (k && !strcmp(k, "package"))
320 k = NULL; /* package is default */
322 if (k) /* if kind!=package, intern <kind>:<name> */
324 int l = strlen(k) + 1 + strlen(n) + 1;
325 if (l > pd->acontent) /* extend buffer if needed */
327 pd->content = (char *)realloc(pd->content, l + 256);
328 pd->acontent = l + 256;
330 sprintf(pd->content, "%s:%s", k, n);
331 name = str2id(pool, pd->content, 1);
334 name = str2id(pool, n, 1); /* package: just intern <name> */
337 if (f) /* operator ? */
340 Id evr = evr_atts2id(pool, pd, atts);
341 /* parser operator to flags */
343 for (flags = 0; flags < sizeof(flagtab)/sizeof(*flagtab); flags++)
344 if (!strcmp(f, flagtab[flags].from))
346 flags = flagtab[flags].to;
352 id = rel2id(pool, name, evr, flags, 1);
355 id = name; /* no operator */
357 /* add new dependency to repo */
358 return repo_addid_dep(pd->repo, olddeps, id, isreq);
362 /*----------------------------------------------------------------*/
371 startElement(void *userData, const char *name, const char **atts)
373 Parsedata *pd = (Parsedata *)userData;
374 struct stateswitch *sw;
375 Pool *pool = pd->pool;
376 Solvable *s = pd->start ? pd->start + pd->pack : 0;
378 if (pd->depth != pd->statedepth)
384 /* ignore deps element */
385 if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
390 /* find node name in stateswitch */
391 for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
393 if (!strcmp(sw->ename, name))
397 /* check if we're at the right level */
398 if (sw->from != pd->state)
401 fprintf(stderr, "into unknown: %s\n", name);
409 pd->docontent = sw->docontent;
410 pd->statedepth = pd->depth;
412 // start with empty content
413 // (will collect data until end element
421 if (pd->kind) /* if kind is set (non package) */
423 strcpy(pd->content, pd->kind);
424 pd->lcontent = strlen(pd->content);
425 pd->content[pd->lcontent++] = ':'; /* prefix name with '<kind>:' */
426 pd->content[pd->lcontent] = 0;
430 case STATE_SUBCHANNEL:
435 case STATE_PACKAGE: /* solvable name */
437 if ((pd->pack & PACK_BLOCK) == 0) /* alloc new block ? */
439 pool->solvables = (Solvable *)realloc(pool->solvables, (pool->nsolvables + pd->pack + PACK_BLOCK + 1) * sizeof(Solvable));
440 pd->start = pool->solvables + pd->repo->start;
441 memset(pd->start + pd->pack, 0, (PACK_BLOCK + 1) * sizeof(Solvable));
444 if (!strcmp(name, "selection"))
445 pd->kind = "selection";
446 else if (!strcmp(name, "pattern"))
447 pd->kind = "pattern";
448 else if (!strcmp(name, "atom"))
450 else if (!strcmp(name, "product"))
451 pd->kind = "product";
452 else if (!strcmp(name, "patch"))
455 pd->kind = NULL; /* default is package */
461 fprintf(stderr, "package #%d\n", pd->pack);
472 case STATE_PROVIDES: /* start of provides */
475 case STATE_PROVIDESENTRY: /* entry within provides */
476 s->provides = adddep(pool, pd, s->provides, atts, 0);
481 case STATE_REQUIRESENTRY:
482 s->requires = adddep(pool, pd, s->requires, atts, 1);
484 case STATE_OBSOLETES:
487 case STATE_OBSOLETESENTRY:
488 s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
490 case STATE_CONFLICTS:
493 case STATE_CONFLICTSENTRY:
494 s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
496 case STATE_RECOMMENDS:
499 case STATE_RECOMMENDSENTRY:
500 s->recommends = adddep(pool, pd, s->recommends, atts, 0);
502 case STATE_SUPPLEMENTS:
505 case STATE_SUPPLEMENTSENTRY:
506 s->supplements = adddep(pool, pd, s->supplements, atts, 0);
511 case STATE_SUGGESTSENTRY:
512 s->suggests = adddep(pool, pd, s->suggests, atts, 0);
517 case STATE_ENHANCESENTRY:
518 s->enhances = adddep(pool, pd, s->enhances, atts, 0);
523 case STATE_FRESHENSENTRY:
524 s->freshens = adddep(pool, pd, s->freshens, atts, 0);
531 static const char* findKernelFlavor(Parsedata *pd, Solvable *s)
533 Pool *pool = pd->pool;
539 for (pidp = pd->repo->idarraydata + s->provides; pidp && (pid = *pidp++) != 0; )
545 continue; /* wrong provides name */
546 prd = GETRELDEP(pool, pid);
547 depname = id2str(pool, prd->name);
548 if (!strncmp(depname, "kernel-", strlen("kernel-")))
550 return depname + strlen("kernel-");
555 //fprintf(stderr, "pack %d\n", pd->pack);
556 //fprintf(stderr, "repo %d\n", s->requires);
561 for (pidp = pd->repo->idarraydata + s->requires ; pidp && (pid = *pidp++) != 0; )
563 const char *depname = 0;
567 depname = id2str(pool, pid);
571 Reldep *prd = GETRELDEP(pool, pid);
572 depname = id2str(pool, prd->name);
574 if (!strncmp(depname, "kernel-", strlen("kernel-")))
576 return depname + strlen("kernel-");
588 * create Solvable from collected data
592 endElement(void *userData, const char *name)
594 Parsedata *pd = (Parsedata *)userData;
595 Pool *pool = pd->pool;
596 Solvable *s = pd->start ? pd->start + pd->pack : NULL;
599 if (pd->depth != pd->statedepth)
602 // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
606 /* ignore deps element */
607 if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
615 case STATE_PACKAGE: /* package complete */
618 if (!s->arch) /* default to "noarch" */
619 s->arch = ARCH_NOARCH;
621 if (!s->evr && pd->version) /* set solvable evr */
622 s->evr = evr2id(pool, pd,
623 pd->epoch ? pd->evrspace + pd->epoch : 0,
624 pd->version ? pd->evrspace + pd->version : 0,
625 pd->release ? pd->evrspace + pd->release : 0);
626 /* ensure self-provides */
627 if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
628 s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
629 s->supplements = repo_fix_legacy(pd->repo, s->provides, s->supplements);
631 const char *flavor = findKernelFlavor(pd, s);
634 char *cflavor = strdup(flavor); /* make pointer safe */
639 /* this is either a kernel package or a kmp */
642 Offset prov = s->provides;
644 while ((pid = pd->repo->idarraydata[prov++]) != 0)
646 const char *depname = 0;
651 prd = GETRELDEP(pool, pid);
652 depname = id2str(pool, prd->name);
656 depname = id2str(pool, pid);
660 if (!strncmp(depname, "kernel(", strlen("kernel(")) && !strchr(depname, ':'))
663 strcpy(newdep, "kernel(");
664 strncat(newdep, cflavor, sizeof(newdep));
665 strncat(newdep, ":", sizeof(newdep));
666 strncat(newdep, depname + strlen("kernel("), 100);
667 pid = str2id(pool, newdep, 1);
669 pid = rel2id(pool, pid, prd->evr, prd->flags, 1);
672 npr = repo_addid_dep(pd->repo, npr, pid, 0);
680 Offset reqs = s->requires;
682 while ((pid = pd->repo->idarraydata[reqs++]) != 0)
684 const char *depname = 0;
689 prd = GETRELDEP(pool, pid);
690 depname = id2str(pool, prd->name);
694 depname = id2str(pool, pid);
697 if (!strncmp(depname, "kernel(", strlen("kernel(")) && !strchr(depname, ':'))
700 strcpy(newdep, "kernel(");
701 strncat(newdep, cflavor, sizeof(newdep));
702 strncat(newdep, ":", sizeof(newdep));
703 strncat(newdep, depname + strlen("kernel("), 100);
704 pid = str2id(pool, newdep, 1);
706 pid = rel2id(pool, pid, prd->evr, prd->flags, 1);
708 npr = repo_addid_dep(pd->repo, npr, pid, 0);
716 pd->pack++; /* inc pack count */
720 s->name = str2id(pool, pd->content, 1);
723 s->vendor = str2id(pool, pd->content, 1);
725 case STATE_UPDATE: /* new version, keeping all other metadata */
726 evr = evr2id(pool, pd,
727 pd->epoch ? pd->evrspace + pd->epoch : 0,
728 pd->version ? pd->evrspace + pd->version : 0,
729 pd->release ? pd->evrspace + pd->release : 0);
734 /* use highest evr */
735 if (!s->evr || evrcmp(pool, s->evr, evr) <= 0)
744 /* ensure buffer space */
745 if (pd->lcontent + 1 + pd->levrspace > pd->aevrspace)
747 pd->evrspace = (char *)realloc(pd->evrspace, pd->lcontent + 1 + pd->levrspace + 256);
748 pd->aevrspace = pd->lcontent + 1 + pd->levrspace + 256;
750 memcpy(pd->evrspace + pd->levrspace, pd->content, pd->lcontent + 1);
751 if (pd->state == STATE_EPOCH || pd->state == STATE_PEPOCH)
752 pd->epoch = pd->levrspace;
753 else if (pd->state == STATE_VERSION || pd->state == STATE_PVERSION)
754 pd->version = pd->levrspace;
756 pd->release = pd->levrspace;
757 pd->levrspace += pd->lcontent + 1;
761 s->arch = str2id(pool, pd->content, 1);
766 pd->state = pd->sbtab[pd->state];
768 // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
779 characterData(void *userData, const XML_Char *s, int len)
781 Parsedata *pd = (Parsedata *)userData;
785 // check if current nodes content is interesting
789 // adapt content buffer
790 l = pd->lcontent + len + 1;
791 if (l > pd->acontent)
793 pd->content = (char *)realloc(pd->content, l + 256);
794 pd->acontent = l + 256;
796 // append new content to buffer
797 c = pd->content + pd->lcontent;
804 /*-------------------------------------------------------------------*/
806 #define BUFF_SIZE 8192
809 * read 'helix' type xml from fp
810 * add packages to pool/repo
815 pool_addrepo_helix(Pool *pool, FILE *fp)
821 struct stateswitch *sw;
824 repo = pool_addrepo_empty(pool);
827 memset(&pd, 0, sizeof(pd));
828 for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
830 if (!pd.swtab[sw->from])
831 pd.swtab[sw->from] = sw;
832 pd.sbtab[sw->to] = sw->from;
838 pd.content = (char *)malloc(256); /* must hold all solvable kinds! */
842 pd.evrspace = (char *)malloc(256);
848 XML_Parser parser = XML_ParserCreate(NULL);
849 XML_SetUserData(parser, &pd); /* make parserdata available to XML callbacks */
850 XML_SetElementHandler(parser, startElement, endElement);
851 XML_SetCharacterDataHandler(parser, characterData);
853 // read/parse XML file
856 l = fread(buf, 1, sizeof(buf), fp);
857 if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
859 fprintf(stderr, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
865 XML_ParserFree(parser);
867 // adapt package count
868 pool->nsolvables += pd.pack;
869 repo->nsolvables = pd.pack;