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
14 * A bit of history: "Helix Code" was the name of the company that
15 * wrote Red Carpet. The company was later renamed to Ximian.
16 * The Red Carpet solver was merged into the ZYPP project, the
17 * library used both by ZENworks and YaST for package management.
18 * Red Carpet came with solver testcases in its own repository
19 * format, the 'helix' format.
23 #include <sys/types.h>
29 #include "solv_xmlparser.h"
30 #include "repo_helix.h"
34 /* XML parser states */
55 STATE_PREREQUIRESENTRY,
61 STATE_RECOMMENDSENTRY,
63 STATE_SUPPLEMENTSENTRY,
80 static struct solv_xmlparser_element stateswitches[] = {
81 { STATE_START, "channel", STATE_CHANNEL, 0 },
82 { STATE_CHANNEL, "subchannel", STATE_SUBCHANNEL, 0 },
83 { STATE_SUBCHANNEL, "package", STATE_PACKAGE, 0 },
84 { STATE_SUBCHANNEL, "srcpackage", STATE_PACKAGE, 0 },
85 { STATE_SUBCHANNEL, "selection", STATE_PACKAGE, 0 },
86 { STATE_SUBCHANNEL, "pattern", STATE_PACKAGE, 0 },
87 { STATE_SUBCHANNEL, "atom", STATE_PACKAGE, 0 },
88 { STATE_SUBCHANNEL, "patch", STATE_PACKAGE, 0 },
89 { STATE_SUBCHANNEL, "product", STATE_PACKAGE, 0 },
90 { STATE_SUBCHANNEL, "application", STATE_PACKAGE, 0 },
91 { STATE_PACKAGE, "name", STATE_NAME, 1 },
92 { STATE_PACKAGE, "vendor", STATE_VENDOR, 1 },
93 { STATE_PACKAGE, "buildtime", STATE_BUILDTIME, 1 },
94 { STATE_PACKAGE, "epoch", STATE_EPOCH, 1 },
95 { STATE_PACKAGE, "version", STATE_VERSION, 1 },
96 { STATE_PACKAGE, "release", STATE_RELEASE, 1 },
97 { STATE_PACKAGE, "arch", STATE_ARCH, 1 },
98 { STATE_PACKAGE, "history", STATE_HISTORY, 0 },
99 { STATE_PACKAGE, "provides", STATE_PROVIDES, 0 },
100 { STATE_PACKAGE, "requires", STATE_REQUIRES, 0 },
101 { STATE_PACKAGE, "prerequires", STATE_PREREQUIRES, 0 },
102 { STATE_PACKAGE, "obsoletes", STATE_OBSOLETES , 0 },
103 { STATE_PACKAGE, "conflicts", STATE_CONFLICTS , 0 },
104 { STATE_PACKAGE, "recommends" , STATE_RECOMMENDS , 0 },
105 { STATE_PACKAGE, "supplements", STATE_SUPPLEMENTS, 0 },
106 { STATE_PACKAGE, "suggests", STATE_SUGGESTS, 0 },
107 { STATE_PACKAGE, "enhances", STATE_ENHANCES, 0 },
108 { STATE_PACKAGE, "freshens", STATE_FRESHENS, 0 },
109 { STATE_PACKAGE, "deps", STATE_PACKAGE, 0 }, /* ignore deps element */
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 },
117 { STATE_PROVIDES, "dep", STATE_PROVIDESENTRY, 0 },
118 { STATE_REQUIRES, "dep", STATE_REQUIRESENTRY, 0 },
119 { STATE_PREREQUIRES, "dep", STATE_PREREQUIRESENTRY, 0 },
120 { STATE_OBSOLETES, "dep", STATE_OBSOLETESENTRY, 0 },
121 { STATE_CONFLICTS, "dep", STATE_CONFLICTSENTRY, 0 },
122 { STATE_RECOMMENDS, "dep", STATE_RECOMMENDSENTRY, 0 },
123 { STATE_SUPPLEMENTS, "dep", STATE_SUPPLEMENTSENTRY, 0 },
124 { STATE_SUGGESTS, "dep", STATE_SUGGESTSENTRY, 0 },
125 { STATE_ENHANCES, "dep", STATE_ENHANCESENTRY, 0 },
126 { STATE_FRESHENS, "dep", STATE_FRESHENSENTRY, 0 },
138 Pool *pool; /* current pool */
139 Repo *repo; /* current repo */
140 Repodata *data; /* current repo data */
141 Solvable *solvable; /* current solvable */
142 Offset freshens; /* current freshens vector */
145 int srcpackage; /* is srcpackage element */
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 solv_xmlparser xmlp;
158 /*------------------------------------------------------------------*/
161 /* create Id from epoch:version-release */
164 evr2id(Pool *pool, struct parsedata *pd, const char *e, const char *v, const char *r)
169 /* treat explitcit 0 as NULL */
170 if (e && (!*e || !strcmp(e, "0")))
176 /* scan version for ":" */
177 for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++) /* skip leading digits */
179 /* if version contains ":", set epoch to "0" */
180 if (v2 > v && *v2 == ':')
184 /* compute length of Id string */
185 l = 1; /* for the \0 */
187 l += strlen(e) + 1; /* e: */
189 l += strlen(v); /* v */
191 l += strlen(r) + 1; /* -r */
193 /* get content space */
194 c = space = solv_xmlparser_contentspace(&pd->xmlp, l);
215 /* if nothing inserted, return Id 0 */
219 fprintf(stderr, "evr: %s\n", space);
221 /* intern and create */
222 return pool_str2id(pool, space, 1);
226 /* create e:v-r from attributes
227 * atts is array of name,value pairs, NULL at end
228 * even index into atts is name
232 evr_atts2id(Pool *pool, struct parsedata *pd, const char **atts)
234 const char *e, *v, *r;
236 for (; *atts; atts += 2)
238 if (!strcmp(*atts, "epoch"))
240 else if (!strcmp(*atts, "version"))
242 else if (!strcmp(*atts, "release"))
245 return evr2id(pool, pd, e, v, r);
248 /*------------------------------------------------------------------*/
249 /* rel operator handling */
256 static struct flagtab flagtab[] = {
259 { ">=", REL_GT|REL_EQ },
261 { "!=", REL_GT|REL_LT },
262 { "<=", REL_LT|REL_EQ },
263 { "(any)", REL_LT|REL_EQ|REL_GT },
267 { "ge", REL_GT|REL_EQ },
269 { "ne", REL_GT|REL_LT },
270 { "le", REL_LT|REL_EQ },
271 { "gte", REL_GT|REL_EQ },
272 { "lte", REL_LT|REL_EQ },
275 { "GE", REL_GT|REL_EQ },
277 { "NE", REL_GT|REL_LT },
278 { "LE", REL_LT|REL_EQ }
282 * process new dependency from parser
283 * olddeps = already collected deps, this defines the 'kind' of dep
284 * atts = array of name,value attributes of dep
285 * isreq == 1 if its a requires
289 adddep(Pool *pool, struct parsedata *pd, unsigned int olddeps, const char **atts, Id marker)
292 const char *n, *f, *k;
297 /* loop over name,value pairs */
298 for (a = atts; *a; a += 2)
300 if (!strcmp(*a, "name"))
302 if (!strcmp(*a, "kind"))
304 else if (!strcmp(*a, "op"))
306 else if (marker && !strcmp(*a, "pre") && a[1][0] == '1')
307 marker = SOLVABLE_PREREQMARKER;
309 if (!n) /* quit if no name found */
313 if (k && !strcmp(k, "package"))
314 k = NULL; /* package is default */
316 if (k) /* if kind!=package, intern <kind>:<name> */
318 int l = strlen(k) + 1 + strlen(n) + 1;
319 char *space = solv_xmlparser_contentspace(&pd->xmlp, l);
320 sprintf(space, "%s:%s", k, n);
321 name = pool_str2id(pool, space, 1);
325 name = pool_str2id(pool, n, 1); /* package: just intern <name> */
328 if (f) /* operator ? */
331 Id evr = evr_atts2id(pool, pd, atts);
332 /* parser operator to flags */
334 for (flags = 0; flags < sizeof(flagtab)/sizeof(*flagtab); flags++)
335 if (!strcmp(f, flagtab[flags].from))
337 flags = flagtab[flags].to;
343 id = pool_rel2id(pool, name, evr, flags, 1);
346 id = name; /* no operator */
348 /* add new dependency to repo */
349 return repo_addid_dep(pd->repo, olddeps, id, marker);
353 /*----------------------------------------------------------------*/
356 startElement(struct solv_xmlparser *xmlp, int state, const char *name, const char **atts)
358 struct parsedata *pd = xmlp->userdata;
359 Pool *pool = pd->pool;
360 Solvable *s = pd->solvable;
366 if (pd->kind) /* if kind is set (non package) */
368 strcpy(xmlp->content, pd->kind);
369 xmlp->lcontent = strlen(xmlp->content);
370 xmlp->content[xmlp->lcontent++] = ':'; /* prefix name with '<kind>:' */
371 xmlp->content[xmlp->lcontent] = 0;
375 case STATE_PACKAGE: /* solvable name */
376 pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
378 pd->kind = NULL; /* default is (src)package */
379 if (!strcmp(name, "selection"))
380 pd->kind = "selection";
381 else if (!strcmp(name, "pattern"))
382 pd->kind = "pattern";
383 else if (!strcmp(name, "atom"))
385 else if (!strcmp(name, "product"))
386 pd->kind = "product";
387 else if (!strcmp(name, "patch"))
389 else if (!strcmp(name, "application"))
390 pd->kind = "application";
391 else if (!strcmp(name, "srcpackage"))
399 fprintf(stderr, "package #%d\n", s - pool->solvables);
410 case STATE_PROVIDES: /* start of provides */
413 case STATE_PROVIDESENTRY: /* entry within provides */
414 s->provides = adddep(pool, pd, s->provides, atts, 0);
416 case STATE_REQUIRESENTRY:
417 s->requires = adddep(pool, pd, s->requires, atts, -SOLVABLE_PREREQMARKER);
419 case STATE_PREREQUIRESENTRY:
420 s->requires = adddep(pool, pd, s->requires, atts, SOLVABLE_PREREQMARKER);
422 case STATE_OBSOLETES:
425 case STATE_OBSOLETESENTRY:
426 s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
428 case STATE_CONFLICTS:
431 case STATE_CONFLICTSENTRY:
432 s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
434 case STATE_RECOMMENDS:
437 case STATE_RECOMMENDSENTRY:
438 s->recommends = adddep(pool, pd, s->recommends, atts, 0);
440 case STATE_SUPPLEMENTS:
443 case STATE_SUPPLEMENTSENTRY:
444 s->supplements = adddep(pool, pd, s->supplements, atts, 0);
449 case STATE_SUGGESTSENTRY:
450 s->suggests = adddep(pool, pd, s->suggests, atts, 0);
455 case STATE_ENHANCESENTRY:
456 s->enhances = adddep(pool, pd, s->enhances, atts, 0);
461 case STATE_FRESHENSENTRY:
462 pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
470 findKernelFlavor(struct parsedata *pd, Solvable *s)
472 Pool *pool = pd->pool;
477 pidp = pd->repo->idarraydata + s->provides;
478 while ((pid = *pidp++) != 0)
484 continue; /* wrong provides name */
485 prd = GETRELDEP(pool, pid);
486 depname = pool_id2str(pool, prd->name);
487 if (!strncmp(depname, "kernel-", 7))
494 pidp = pd->repo->idarraydata + s->requires;
495 while ((pid = *pidp++) != 0)
501 depname = pool_id2str(pool, pid);
505 Reldep *prd = GETRELDEP(pool, pid);
506 depname = pool_id2str(pool, prd->name);
508 if (!strncmp(depname, "kernel-", 7))
518 endElement(struct solv_xmlparser *xmlp, int state, char *content)
520 struct parsedata *pd = xmlp->userdata;
521 Pool *pool = pd->pool;
522 Solvable *s = pd->solvable;
530 case STATE_PACKAGE: /* package complete */
531 if (pd->srcpackage && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
533 if (!s->arch) /* default to "noarch" */
534 s->arch = ARCH_NOARCH;
536 if (!s->evr && pd->version) /* set solvable evr */
537 s->evr = evr2id(pool, pd,
538 pd->epoch ? pd->evrspace + pd->epoch : 0,
539 pd->version ? pd->evrspace + pd->version : 0,
540 pd->release ? pd->evrspace + pd->release : 0);
541 /* ensure self-provides */
542 if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
543 s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
544 repo_rewrite_suse_deps(s, pd->freshens);
547 /* see bugzilla bnc#190163 */
548 flavor = findKernelFlavor(pd, s);
551 char *cflavor = solv_strdup(flavor); /* make pointer safe */
556 /* this is either a kernel package or a kmp */
559 Offset prov = s->provides;
561 while ((pid = pd->repo->idarraydata[prov++]) != 0)
563 const char *depname = 0;
568 prd = GETRELDEP(pool, pid);
569 depname = pool_id2str(pool, prd->name);
573 depname = pool_id2str(pool, pid);
577 if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
580 snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
581 pid = pool_str2id(pool, newdep, 1);
583 pid = pool_rel2id(pool, pid, prd->evr, prd->flags, 1);
586 npr = repo_addid_dep(pd->repo, npr, pid, 0);
594 Offset reqs = s->requires;
596 while ((pid = pd->repo->idarraydata[reqs++]) != 0)
598 const char *depname = 0;
603 prd = GETRELDEP(pool, pid);
604 depname = pool_id2str(pool, prd->name);
608 depname = pool_id2str(pool, pid);
611 if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
614 snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
615 pid = pool_str2id(pool, newdep, 1);
617 pid = pool_rel2id(pool, pid, prd->evr, prd->flags, 1);
619 npr = repo_addid_dep(pd->repo, npr, pid, 0);
628 s->name = pool_str2id(pool, content, 1);
631 s->vendor = pool_str2id(pool, content, 1);
633 case STATE_BUILDTIME:
636 repodata_set_num(pd->data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
638 case STATE_UPDATE: /* new version, keeping all other metadata */
639 evr = evr2id(pool, pd,
640 pd->epoch ? pd->evrspace + pd->epoch : 0,
641 pd->version ? pd->evrspace + pd->version : 0,
642 pd->release ? pd->evrspace + pd->release : 0);
647 /* use highest evr */
648 if (!s->evr || pool_evrcmp(pool, s->evr, evr, EVRCMP_COMPARE) <= 0)
654 /* ensure buffer space */
655 if (xmlp->lcontent + 1 + pd->levrspace > pd->aevrspace)
657 pd->aevrspace = xmlp->lcontent + 1 + pd->levrspace + 256;
658 pd->evrspace = (char *)realloc(pd->evrspace, pd->aevrspace);
660 memcpy(pd->evrspace + pd->levrspace, xmlp->content, xmlp->lcontent + 1);
661 if (state == STATE_EPOCH)
662 pd->epoch = pd->levrspace;
663 else if (state == STATE_VERSION)
664 pd->version = pd->levrspace;
666 pd->release = pd->levrspace;
667 pd->levrspace += xmlp->lcontent + 1;
670 s->arch = pool_str2id(pool, content, 1);
677 /*-------------------------------------------------------------------*/
680 * read 'helix' type xml from fp
681 * add packages to pool/repo
686 repo_add_helix(Repo *repo, FILE *fp, int flags)
688 Pool *pool = repo->pool;
693 now = solv_timems(0);
694 data = repo_add_repodata(repo, flags);
696 /* prepare parsedata */
697 memset(&pd, 0, sizeof(pd));
702 pd.evrspace = (char *)solv_malloc(256);
706 solv_xmlparser_init(&pd.xmlp, stateswitches, &pd, startElement, endElement);
707 if (solv_xmlparser_parse(&pd.xmlp, fp) != SOLV_XMLPARSER_OK)
708 pd.ret = pool_error(pd.pool, -1, "repo_helix: %s at line %u", pd.xmlp.errstr, pd.xmlp.line);
709 solv_xmlparser_free(&pd.xmlp);
711 solv_free(pd.evrspace);
713 if (!(flags & REPO_NO_INTERNALIZE))
714 repodata_internalize(data);
715 POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_helix took %d ms\n", solv_timems(now));
716 POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
717 POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", repodata_memused(data)/1024, repo->idarraysize / (int)(1024/sizeof(Id)));