2 * Copyright (c) 2007, Novell Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
11 * Parse 'helix' XML representation
16 #include <sys/types.h>
24 #include "repo_helix.h"
28 /* XML parser states */
49 STATE_PREREQUIRESENTRY,
55 STATE_RECOMMENDSENTRY,
57 STATE_SUPPLEMENTSENTRY,
86 static struct stateswitch stateswitches[] = {
87 { STATE_START, "channel", STATE_CHANNEL, 0 },
88 { STATE_CHANNEL, "subchannel", STATE_SUBCHANNEL, 0 },
89 { STATE_SUBCHANNEL, "package", STATE_PACKAGE, 0 },
90 { STATE_SUBCHANNEL, "selection", STATE_PACKAGE, 0 },
91 { STATE_SUBCHANNEL, "pattern", STATE_PACKAGE, 0 },
92 { STATE_SUBCHANNEL, "atom", STATE_PACKAGE, 0 },
93 { STATE_SUBCHANNEL, "patch", STATE_PACKAGE, 0 },
94 { STATE_SUBCHANNEL, "product", STATE_PACKAGE, 0 },
95 { STATE_PACKAGE, "name", STATE_NAME, 1 },
96 { STATE_PACKAGE, "vendor", STATE_VENDOR, 1 },
97 { STATE_PACKAGE, "buildtime", STATE_BUILDTIME, 1 },
98 { STATE_PACKAGE, "epoch", STATE_PEPOCH, 1 },
99 { STATE_PACKAGE, "version", STATE_PVERSION, 1 },
100 { STATE_PACKAGE, "release", STATE_PRELEASE, 1 },
101 { STATE_PACKAGE, "arch", STATE_PARCH, 1 },
102 { STATE_PACKAGE, "history", STATE_HISTORY, 0 },
103 { STATE_PACKAGE, "provides", STATE_PROVIDES, 0 },
104 { STATE_PACKAGE, "requires", STATE_REQUIRES, 0 },
105 { STATE_PACKAGE, "prerequires", STATE_PREREQUIRES, 0 },
106 { STATE_PACKAGE, "obsoletes", STATE_OBSOLETES , 0 },
107 { STATE_PACKAGE, "conflicts", STATE_CONFLICTS , 0 },
108 { STATE_PACKAGE, "recommends" , STATE_RECOMMENDS , 0 },
109 { STATE_PACKAGE, "supplements", STATE_SUPPLEMENTS, 0 },
110 { STATE_PACKAGE, "suggests", STATE_SUGGESTS, 0 },
111 { STATE_PACKAGE, "enhances", STATE_ENHANCES, 0 },
112 { STATE_PACKAGE, "freshens", STATE_FRESHENS, 0 },
114 { STATE_HISTORY, "update", STATE_UPDATE, 0 },
115 { STATE_UPDATE, "epoch", STATE_EPOCH, 1 },
116 { STATE_UPDATE, "version", STATE_VERSION, 1 },
117 { STATE_UPDATE, "release", STATE_RELEASE, 1 },
118 { STATE_UPDATE, "arch", STATE_ARCH, 1 },
120 { STATE_PROVIDES, "dep", STATE_PROVIDESENTRY, 0 },
121 { STATE_REQUIRES, "dep", STATE_REQUIRESENTRY, 0 },
122 { STATE_PREREQUIRES, "dep", STATE_PREREQUIRESENTRY, 0 },
123 { STATE_OBSOLETES, "dep", STATE_OBSOLETESENTRY, 0 },
124 { STATE_CONFLICTS, "dep", STATE_CONFLICTSENTRY, 0 },
125 { STATE_RECOMMENDS, "dep", STATE_RECOMMENDSENTRY, 0 },
126 { STATE_SUPPLEMENTS, "dep", STATE_SUPPLEMENTSENTRY, 0 },
127 { STATE_SUGGESTS, "dep", STATE_SUGGESTSENTRY, 0 },
128 { STATE_ENHANCES, "dep", STATE_ENHANCESENTRY, 0 },
129 { STATE_FRESHENS, "dep", STATE_FRESHENSENTRY, 0 },
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 Pool *pool; // current pool
150 Repo *repo; // current repo
151 Repodata *data; // current repo data
152 Solvable *solvable; // current solvable
153 Offset freshens; // current freshens vector
156 int epoch; // epoch (as offset into evrspace)
157 int version; // version (as offset into evrspace)
158 int release; // release (as offset into evrspace)
159 char *evrspace; // buffer for evr
160 int aevrspace; // actual buffer space
161 int levrspace; // actual evr length
164 struct stateswitch *swtab[NUMSTATES];
165 enum state sbtab[NUMSTATES];
169 /*------------------------------------------------------------------*/
172 // create Id from epoch:version-release
175 evr2id(Pool *pool, Parsedata *pd, const char *e, const char *v, const char *r)
180 // treat explitcit 0 as NULL
181 if (e && !strcmp(e, "0"))
187 // scan version for ":"
188 for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++) // skip leading digits
190 // if version contains ":", set epoch to "0"
191 if (v2 > v && *v2 == ':')
195 // compute length of Id string
198 l += strlen(e) + 1; // e:
202 l += strlen(r) + 1; // -r
204 // extend content if not sufficient
205 if (l > pd->acontent)
207 pd->content = (char *)realloc(pd->content, l + 256);
208 pd->acontent = l + 256;
211 // copy e-v-r to content
231 // if nothing inserted, return Id 0
235 fprintf(stderr, "evr: %s\n", pd->content);
238 return str2id(pool, pd->content, 1);
242 // create e:v-r from attributes
243 // atts is array of name,value pairs, NULL at end
244 // even index into atts is name
245 // odd index is value
248 evr_atts2id(Pool *pool, Parsedata *pd, const char **atts)
250 const char *e, *v, *r;
252 for (; *atts; atts += 2)
254 if (!strcmp(*atts, "epoch"))
256 else if (!strcmp(*atts, "version"))
258 else if (!strcmp(*atts, "release"))
261 return evr2id(pool, pd, e, v, r);
264 /*------------------------------------------------------------------*/
265 /* rel operator handling */
272 static struct flagtab flagtab[] = {
275 { ">=", REL_GT|REL_EQ },
277 { "!=", REL_GT|REL_LT },
278 { "<=", REL_LT|REL_EQ },
279 { "(any)", REL_LT|REL_EQ|REL_GT },
283 { "ge", REL_GT|REL_EQ },
285 { "ne", REL_GT|REL_LT },
286 { "le", REL_LT|REL_EQ },
287 { "gte", REL_GT|REL_EQ },
288 { "lte", REL_LT|REL_EQ },
291 { "GE", REL_GT|REL_EQ },
293 { "NE", REL_GT|REL_LT },
294 { "LE", REL_LT|REL_EQ }
298 * process new dependency from parser
299 * olddeps = already collected deps, this defines the 'kind' of dep
300 * atts = array of name,value attributes of dep
301 * isreq == 1 if its a requires
305 adddep(Pool *pool, Parsedata *pd, unsigned int olddeps, const char **atts, Id marker)
308 const char *n, *f, *k;
313 /* loop over name,value pairs */
314 for (a = atts; *a; a += 2)
316 if (!strcmp(*a, "name"))
318 if (!strcmp(*a, "kind"))
320 else if (!strcmp(*a, "op"))
322 else if (marker && !strcmp(*a, "pre") && a[1][0] == '1')
323 marker = SOLVABLE_PREREQMARKER;
325 if (!n) /* quit if no name found */
329 if (k && !strcmp(k, "package"))
330 k = NULL; /* package is default */
332 if (k) /* if kind!=package, intern <kind>:<name> */
334 int l = strlen(k) + 1 + strlen(n) + 1;
335 if (l > pd->acontent) /* extend buffer if needed */
337 pd->content = (char *)realloc(pd->content, l + 256);
338 pd->acontent = l + 256;
340 sprintf(pd->content, "%s:%s", k, n);
341 name = str2id(pool, pd->content, 1);
345 name = str2id(pool, n, 1); /* package: just intern <name> */
348 if (f) /* operator ? */
351 Id evr = evr_atts2id(pool, pd, atts);
352 /* parser operator to flags */
354 for (flags = 0; flags < sizeof(flagtab)/sizeof(*flagtab); flags++)
355 if (!strcmp(f, flagtab[flags].from))
357 flags = flagtab[flags].to;
363 id = rel2id(pool, name, evr, flags, 1);
366 id = name; /* no operator */
368 /* add new dependency to repo */
369 return repo_addid_dep(pd->repo, olddeps, id, marker);
373 /*----------------------------------------------------------------*/
382 startElement(void *userData, const char *name, const char **atts)
384 Parsedata *pd = (Parsedata *)userData;
385 struct stateswitch *sw;
386 Pool *pool = pd->pool;
387 Solvable *s = pd->solvable;
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 if (!pd->swtab[pd->state])
404 for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
406 if (!strcmp(sw->ename, name))
410 /* check if we're at the right level */
411 if (sw->from != pd->state)
414 fprintf(stderr, "into unknown: %s\n", name);
422 pd->docontent = sw->docontent;
423 pd->statedepth = pd->depth;
425 // start with empty content
426 // (will collect data until end element
434 if (pd->kind) /* if kind is set (non package) */
436 strcpy(pd->content, pd->kind);
437 pd->lcontent = strlen(pd->content);
438 pd->content[pd->lcontent++] = ':'; /* prefix name with '<kind>:' */
439 pd->content[pd->lcontent] = 0;
443 case STATE_PACKAGE: /* solvable name */
444 pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
445 if (!strcmp(name, "selection"))
446 pd->kind = "selection";
447 else if (!strcmp(name, "pattern"))
448 pd->kind = "pattern";
449 else if (!strcmp(name, "atom"))
451 else if (!strcmp(name, "product"))
452 pd->kind = "product";
453 else if (!strcmp(name, "patch"))
456 pd->kind = NULL; /* default is package */
463 fprintf(stderr, "package #%d\n", s - pool->solvables);
474 case STATE_PROVIDES: /* start of provides */
477 case STATE_PROVIDESENTRY: /* entry within provides */
478 s->provides = adddep(pool, pd, s->provides, atts, 0);
480 case STATE_REQUIRESENTRY:
481 s->requires = adddep(pool, pd, s->requires, atts, -SOLVABLE_PREREQMARKER);
483 case STATE_PREREQUIRESENTRY:
484 s->requires = adddep(pool, pd, s->requires, atts, SOLVABLE_PREREQMARKER);
486 case STATE_OBSOLETES:
489 case STATE_OBSOLETESENTRY:
490 s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
492 case STATE_CONFLICTS:
495 case STATE_CONFLICTSENTRY:
496 s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
498 case STATE_RECOMMENDS:
501 case STATE_RECOMMENDSENTRY:
502 s->recommends = adddep(pool, pd, s->recommends, atts, 0);
504 case STATE_SUPPLEMENTS:
507 case STATE_SUPPLEMENTSENTRY:
508 s->supplements = adddep(pool, pd, s->supplements, atts, 0);
513 case STATE_SUGGESTSENTRY:
514 s->suggests = adddep(pool, pd, s->suggests, atts, 0);
519 case STATE_ENHANCESENTRY:
520 s->enhances = adddep(pool, pd, s->enhances, atts, 0);
525 case STATE_FRESHENSENTRY:
526 pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
533 static const char *findKernelFlavor(Parsedata *pd, Solvable *s)
535 Pool *pool = pd->pool;
540 pidp = pd->repo->idarraydata + s->provides;
541 while ((pid = *pidp++) != 0)
547 continue; /* wrong provides name */
548 prd = GETRELDEP(pool, pid);
549 depname = id2str(pool, prd->name);
550 if (!strncmp(depname, "kernel-", 7))
557 pidp = pd->repo->idarraydata + s->requires;
558 while ((pid = *pidp++) != 0)
564 depname = id2str(pool, pid);
568 Reldep *prd = GETRELDEP(pool, pid);
569 depname = id2str(pool, prd->name);
571 if (!strncmp(depname, "kernel-", 7))
584 * create Solvable from collected data
588 endElement(void *userData, const char *name)
590 Parsedata *pd = (Parsedata *)userData;
591 Pool *pool = pd->pool;
592 Solvable *s = pd->solvable;
596 if (pd->depth != pd->statedepth)
599 // printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth);
603 /* ignore deps element */
604 if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
612 case STATE_PACKAGE: /* package complete */
613 if (!s->arch) /* default to "noarch" */
614 s->arch = ARCH_NOARCH;
616 if (!s->evr && pd->version) /* set solvable evr */
617 s->evr = evr2id(pool, pd,
618 pd->epoch ? pd->evrspace + pd->epoch : 0,
619 pd->version ? pd->evrspace + pd->version : 0,
620 pd->release ? pd->evrspace + pd->release : 0);
621 /* ensure self-provides */
622 if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
623 s->provides = repo_addid_dep(pd->repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
624 s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, pd->freshens);
625 s->conflicts = repo_fix_conflicts(pd->repo, s->conflicts);
628 /* see bugzilla bnc#190163 */
629 const char *flavor = findKernelFlavor(pd, s);
632 char *cflavor = strdup(flavor); /* make pointer safe */
637 /* this is either a kernel package or a kmp */
640 Offset prov = s->provides;
642 while ((pid = pd->repo->idarraydata[prov++]) != 0)
644 const char *depname = 0;
649 prd = GETRELDEP(pool, pid);
650 depname = id2str(pool, prd->name);
654 depname = id2str(pool, pid);
658 if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
661 snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
662 pid = str2id(pool, newdep, 1);
664 pid = rel2id(pool, pid, prd->evr, prd->flags, 1);
667 npr = repo_addid_dep(pd->repo, npr, pid, 0);
675 Offset reqs = s->requires;
677 while ((pid = pd->repo->idarraydata[reqs++]) != 0)
679 const char *depname = 0;
684 prd = GETRELDEP(pool, pid);
685 depname = id2str(pool, prd->name);
689 depname = id2str(pool, pid);
692 if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
695 snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
696 pid = str2id(pool, newdep, 1);
698 pid = rel2id(pool, pid, prd->evr, prd->flags, 1);
700 npr = repo_addid_dep(pd->repo, npr, pid, 0);
709 s->name = str2id(pool, pd->content, 1);
712 s->vendor = str2id(pool, pd->content, 1);
714 case STATE_BUILDTIME:
715 t = atoi (pd->content);
717 repodata_set_num(pd->data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
719 case STATE_UPDATE: /* new version, keeping all other metadata */
720 evr = evr2id(pool, pd,
721 pd->epoch ? pd->evrspace + pd->epoch : 0,
722 pd->version ? pd->evrspace + pd->version : 0,
723 pd->release ? pd->evrspace + pd->release : 0);
728 /* use highest evr */
729 if (!s->evr || evrcmp(pool, s->evr, evr, EVRCMP_MATCH_RELEASE) <= 0)
738 /* ensure buffer space */
739 if (pd->lcontent + 1 + pd->levrspace > pd->aevrspace)
741 pd->evrspace = (char *)realloc(pd->evrspace, pd->lcontent + 1 + pd->levrspace + 256);
742 pd->aevrspace = pd->lcontent + 1 + pd->levrspace + 256;
744 memcpy(pd->evrspace + pd->levrspace, pd->content, pd->lcontent + 1);
745 if (pd->state == STATE_EPOCH || pd->state == STATE_PEPOCH)
746 pd->epoch = pd->levrspace;
747 else if (pd->state == STATE_VERSION || pd->state == STATE_PVERSION)
748 pd->version = pd->levrspace;
750 pd->release = pd->levrspace;
751 pd->levrspace += pd->lcontent + 1;
755 s->arch = str2id(pool, pd->content, 1);
760 pd->state = pd->sbtab[pd->state];
762 // printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth);
773 characterData(void *userData, const XML_Char *s, int len)
775 Parsedata *pd = (Parsedata *)userData;
779 // check if current nodes content is interesting
783 // adapt content buffer
784 l = pd->lcontent + len + 1;
785 if (l > pd->acontent)
787 pd->content = (char *)realloc(pd->content, l + 256);
788 pd->acontent = l + 256;
790 // append new content to buffer
791 c = pd->content + pd->lcontent;
798 /*-------------------------------------------------------------------*/
800 #define BUFF_SIZE 8192
803 * read 'helix' type xml from fp
804 * add packages to pool/repo
809 repo_add_helix(Repo *repo, FILE *fp, int flags)
811 Pool *pool = repo->pool;
816 struct stateswitch *sw;
820 if (!(flags & REPO_REUSE_REPODATA))
821 data = repo_add_repodata(repo, 0);
823 data = repo_last_repodata(repo);
825 /* prepare parsedata */
826 memset(&pd, 0, sizeof(pd));
827 for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
829 if (!pd.swtab[sw->from])
830 pd.swtab[sw->from] = sw;
831 pd.sbtab[sw->to] = sw->from;
837 pd.content = (char *)malloc(256); /* must hold all solvable kinds! */
841 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 pool_debug(pool, SAT_FATAL, "%s at line %u\n", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
865 XML_ParserFree(parser);
869 if (!(flags & REPO_NO_INTERNALIZE))
870 repodata_internalize(data);
871 POOL_DEBUG(SAT_DEBUG_STATS, "repo_add_helix took %d ms\n", sat_timems(now));
872 POOL_DEBUG(SAT_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
873 POOL_DEBUG(SAT_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", data->incoredatalen/1024, repo->idarraysize / (int)(1024/sizeof(Id)));