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 it's own repository
19 * format, the 'helix' format.
23 #include <sys/types.h>
31 #include "repo_helix.h"
35 /* XML parser states */
56 STATE_PREREQUIRESENTRY,
62 STATE_RECOMMENDSENTRY,
64 STATE_SUPPLEMENTSENTRY,
93 static struct stateswitch stateswitches[] = {
94 { STATE_START, "channel", STATE_CHANNEL, 0 },
95 { STATE_CHANNEL, "subchannel", STATE_SUBCHANNEL, 0 },
96 { STATE_SUBCHANNEL, "package", STATE_PACKAGE, 0 },
97 { STATE_SUBCHANNEL, "srcpackage", STATE_PACKAGE, 0 },
98 { STATE_SUBCHANNEL, "selection", STATE_PACKAGE, 0 },
99 { STATE_SUBCHANNEL, "pattern", STATE_PACKAGE, 0 },
100 { STATE_SUBCHANNEL, "atom", STATE_PACKAGE, 0 },
101 { STATE_SUBCHANNEL, "patch", STATE_PACKAGE, 0 },
102 { STATE_SUBCHANNEL, "product", STATE_PACKAGE, 0 },
103 { STATE_PACKAGE, "name", STATE_NAME, 1 },
104 { STATE_PACKAGE, "vendor", STATE_VENDOR, 1 },
105 { STATE_PACKAGE, "buildtime", STATE_BUILDTIME, 1 },
106 { STATE_PACKAGE, "epoch", STATE_PEPOCH, 1 },
107 { STATE_PACKAGE, "version", STATE_PVERSION, 1 },
108 { STATE_PACKAGE, "release", STATE_PRELEASE, 1 },
109 { STATE_PACKAGE, "arch", STATE_PARCH, 1 },
110 { STATE_PACKAGE, "history", STATE_HISTORY, 0 },
111 { STATE_PACKAGE, "provides", STATE_PROVIDES, 0 },
112 { STATE_PACKAGE, "requires", STATE_REQUIRES, 0 },
113 { STATE_PACKAGE, "prerequires", STATE_PREREQUIRES, 0 },
114 { STATE_PACKAGE, "obsoletes", STATE_OBSOLETES , 0 },
115 { STATE_PACKAGE, "conflicts", STATE_CONFLICTS , 0 },
116 { STATE_PACKAGE, "recommends" , STATE_RECOMMENDS , 0 },
117 { STATE_PACKAGE, "supplements", STATE_SUPPLEMENTS, 0 },
118 { STATE_PACKAGE, "suggests", STATE_SUGGESTS, 0 },
119 { STATE_PACKAGE, "enhances", STATE_ENHANCES, 0 },
120 { STATE_PACKAGE, "freshens", STATE_FRESHENS, 0 },
122 { STATE_HISTORY, "update", STATE_UPDATE, 0 },
123 { STATE_UPDATE, "epoch", STATE_EPOCH, 1 },
124 { STATE_UPDATE, "version", STATE_VERSION, 1 },
125 { STATE_UPDATE, "release", STATE_RELEASE, 1 },
126 { STATE_UPDATE, "arch", STATE_ARCH, 1 },
128 { STATE_PROVIDES, "dep", STATE_PROVIDESENTRY, 0 },
129 { STATE_REQUIRES, "dep", STATE_REQUIRESENTRY, 0 },
130 { STATE_PREREQUIRES, "dep", STATE_PREREQUIRESENTRY, 0 },
131 { STATE_OBSOLETES, "dep", STATE_OBSOLETESENTRY, 0 },
132 { STATE_CONFLICTS, "dep", STATE_CONFLICTSENTRY, 0 },
133 { STATE_RECOMMENDS, "dep", STATE_RECOMMENDSENTRY, 0 },
134 { STATE_SUPPLEMENTS, "dep", STATE_SUPPLEMENTSENTRY, 0 },
135 { STATE_SUGGESTS, "dep", STATE_SUGGESTSENTRY, 0 },
136 { STATE_ENHANCES, "dep", STATE_ENHANCESENTRY, 0 },
137 { STATE_FRESHENS, "dep", STATE_FRESHENSENTRY, 0 },
146 typedef struct _parsedata {
148 /* XML parser data */
150 enum state state; /* current state */
152 char *content; /* buffer for content of node */
153 int lcontent; /* actual length of current content */
154 int acontent; /* actual buffer size */
155 int docontent; /* handle content */
158 Pool *pool; /* current pool */
159 Repo *repo; /* current repo */
160 Repodata *data; /* current repo data */
161 Solvable *solvable; /* current solvable */
162 Offset freshens; /* current freshens vector */
165 int epoch; /* epoch (as offset into evrspace) */
166 int version; /* version (as offset into evrspace) */
167 int release; /* release (as offset into evrspace) */
168 char *evrspace; /* buffer for evr */
169 int aevrspace; /* actual buffer space */
170 int levrspace; /* actual evr length */
173 struct stateswitch *swtab[NUMSTATES];
174 enum state sbtab[NUMSTATES];
178 /*------------------------------------------------------------------*/
181 /* create Id from epoch:version-release */
184 evr2id(Pool *pool, Parsedata *pd, const char *e, const char *v, const char *r)
189 /* treat explitcit 0 as NULL */
190 if (e && (!*e || !strcmp(e, "0")))
196 /* scan version for ":" */
197 for (v2 = v; *v2 >= '0' && *v2 <= '9'; v2++) /* skip leading digits */
199 /* if version contains ":", set epoch to "0" */
200 if (v2 > v && *v2 == ':')
204 /* compute length of Id string */
205 l = 1; /* for the \0 */
207 l += strlen(e) + 1; /* e: */
209 l += strlen(v); /* v */
211 l += strlen(r) + 1; /* -r */
213 /* extend content if not sufficient */
214 if (l > pd->acontent)
216 pd->content = (char *)realloc(pd->content, l + 256);
217 pd->acontent = l + 256;
220 /* copy e-v-r to content */
240 /* if nothing inserted, return Id 0 */
244 fprintf(stderr, "evr: %s\n", pd->content);
246 /* intern and create */
247 return pool_str2id(pool, pd->content, 1);
251 /* create e:v-r from attributes
252 * atts is array of name,value pairs, NULL at end
253 * even index into atts is name
257 evr_atts2id(Pool *pool, Parsedata *pd, const char **atts)
259 const char *e, *v, *r;
261 for (; *atts; atts += 2)
263 if (!strcmp(*atts, "epoch"))
265 else if (!strcmp(*atts, "version"))
267 else if (!strcmp(*atts, "release"))
270 return evr2id(pool, pd, e, v, r);
273 /*------------------------------------------------------------------*/
274 /* rel operator handling */
281 static struct flagtab flagtab[] = {
284 { ">=", REL_GT|REL_EQ },
286 { "!=", REL_GT|REL_LT },
287 { "<=", REL_LT|REL_EQ },
288 { "(any)", REL_LT|REL_EQ|REL_GT },
292 { "ge", REL_GT|REL_EQ },
294 { "ne", REL_GT|REL_LT },
295 { "le", REL_LT|REL_EQ },
296 { "gte", REL_GT|REL_EQ },
297 { "lte", REL_LT|REL_EQ },
300 { "GE", REL_GT|REL_EQ },
302 { "NE", REL_GT|REL_LT },
303 { "LE", REL_LT|REL_EQ }
307 * process new dependency from parser
308 * olddeps = already collected deps, this defines the 'kind' of dep
309 * atts = array of name,value attributes of dep
310 * isreq == 1 if its a requires
314 adddep(Pool *pool, Parsedata *pd, unsigned int olddeps, const char **atts, Id marker)
317 const char *n, *f, *k;
322 /* loop over name,value pairs */
323 for (a = atts; *a; a += 2)
325 if (!strcmp(*a, "name"))
327 if (!strcmp(*a, "kind"))
329 else if (!strcmp(*a, "op"))
331 else if (marker && !strcmp(*a, "pre") && a[1][0] == '1')
332 marker = SOLVABLE_PREREQMARKER;
334 if (!n) /* quit if no name found */
338 if (k && !strcmp(k, "package"))
339 k = NULL; /* package is default */
341 if (k) /* if kind!=package, intern <kind>:<name> */
343 int l = strlen(k) + 1 + strlen(n) + 1;
344 if (l > pd->acontent) /* extend buffer if needed */
346 pd->content = (char *)realloc(pd->content, l + 256);
347 pd->acontent = l + 256;
349 sprintf(pd->content, "%s:%s", k, n);
350 name = pool_str2id(pool, pd->content, 1);
354 name = pool_str2id(pool, n, 1); /* package: just intern <name> */
357 if (f) /* operator ? */
360 Id evr = evr_atts2id(pool, pd, atts);
361 /* parser operator to flags */
363 for (flags = 0; flags < sizeof(flagtab)/sizeof(*flagtab); flags++)
364 if (!strcmp(f, flagtab[flags].from))
366 flags = flagtab[flags].to;
372 id = pool_rel2id(pool, name, evr, flags, 1);
375 id = name; /* no operator */
377 /* add new dependency to repo */
378 return repo_addid_dep(pd->repo, olddeps, id, marker);
382 /*----------------------------------------------------------------*/
391 startElement(void *userData, const char *name, const char **atts)
393 Parsedata *pd = (Parsedata *)userData;
394 struct stateswitch *sw;
395 Pool *pool = pd->pool;
396 Solvable *s = pd->solvable;
398 if (pd->depth != pd->statedepth)
404 /* ignore deps element */
405 if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
410 /* find node name in stateswitch */
411 if (!pd->swtab[pd->state])
413 for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++)
415 if (!strcmp(sw->ename, name))
419 /* check if we're at the right level */
420 if (sw->from != pd->state)
423 fprintf(stderr, "into unknown: %s\n", name);
431 pd->docontent = sw->docontent;
432 pd->statedepth = pd->depth;
434 /* start with empty content */
435 /* (will collect data until end element) */
443 if (pd->kind) /* if kind is set (non package) */
445 strcpy(pd->content, pd->kind);
446 pd->lcontent = strlen(pd->content);
447 pd->content[pd->lcontent++] = ':'; /* prefix name with '<kind>:' */
448 pd->content[pd->lcontent] = 0;
452 case STATE_PACKAGE: /* solvable name */
453 pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo));
454 if (!strcmp(name, "selection"))
455 pd->kind = "selection";
456 else if (!strcmp(name, "pattern"))
457 pd->kind = "pattern";
458 else if (!strcmp(name, "atom"))
460 else if (!strcmp(name, "product"))
461 pd->kind = "product";
462 else if (!strcmp(name, "patch"))
465 pd->kind = NULL; /* default is package */
472 fprintf(stderr, "package #%d\n", s - pool->solvables);
483 case STATE_PROVIDES: /* start of provides */
486 case STATE_PROVIDESENTRY: /* entry within provides */
487 s->provides = adddep(pool, pd, s->provides, atts, 0);
489 case STATE_REQUIRESENTRY:
490 s->requires = adddep(pool, pd, s->requires, atts, -SOLVABLE_PREREQMARKER);
492 case STATE_PREREQUIRESENTRY:
493 s->requires = adddep(pool, pd, s->requires, atts, SOLVABLE_PREREQMARKER);
495 case STATE_OBSOLETES:
498 case STATE_OBSOLETESENTRY:
499 s->obsoletes = adddep(pool, pd, s->obsoletes, atts, 0);
501 case STATE_CONFLICTS:
504 case STATE_CONFLICTSENTRY:
505 s->conflicts = adddep(pool, pd, s->conflicts, atts, 0);
507 case STATE_RECOMMENDS:
510 case STATE_RECOMMENDSENTRY:
511 s->recommends = adddep(pool, pd, s->recommends, atts, 0);
513 case STATE_SUPPLEMENTS:
516 case STATE_SUPPLEMENTSENTRY:
517 s->supplements = adddep(pool, pd, s->supplements, atts, 0);
522 case STATE_SUGGESTSENTRY:
523 s->suggests = adddep(pool, pd, s->suggests, atts, 0);
528 case STATE_ENHANCESENTRY:
529 s->enhances = adddep(pool, pd, s->enhances, atts, 0);
534 case STATE_FRESHENSENTRY:
535 pd->freshens = adddep(pool, pd, pd->freshens, atts, 0);
542 static const char *findKernelFlavor(Parsedata *pd, Solvable *s)
544 Pool *pool = pd->pool;
549 pidp = pd->repo->idarraydata + s->provides;
550 while ((pid = *pidp++) != 0)
556 continue; /* wrong provides name */
557 prd = GETRELDEP(pool, pid);
558 depname = pool_id2str(pool, prd->name);
559 if (!strncmp(depname, "kernel-", 7))
566 pidp = pd->repo->idarraydata + s->requires;
567 while ((pid = *pidp++) != 0)
573 depname = pool_id2str(pool, pid);
577 Reldep *prd = GETRELDEP(pool, pid);
578 depname = pool_id2str(pool, prd->name);
580 if (!strncmp(depname, "kernel-", 7))
593 * create Solvable from collected data
597 endElement(void *userData, const char *name)
599 Parsedata *pd = (Parsedata *)userData;
600 Pool *pool = pd->pool;
601 Solvable *s = pd->solvable;
606 if (pd->depth != pd->statedepth)
609 /* printf("back from unknown %d %d %d\n", pd->state, pd->depth, pd->statedepth); */
613 /* ignore deps element */
614 if (pd->state == STATE_PACKAGE && !strcmp(name, "deps"))
622 case STATE_PACKAGE: /* package complete */
623 if (name[0] == 's' && name[1] == 'r' && name[2] == 'c' && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
625 if (!s->arch) /* default to "noarch" */
626 s->arch = ARCH_NOARCH;
628 if (!s->evr && pd->version) /* set solvable evr */
629 s->evr = evr2id(pool, pd,
630 pd->epoch ? pd->evrspace + pd->epoch : 0,
631 pd->version ? pd->evrspace + pd->version : 0,
632 pd->release ? pd->evrspace + pd->release : "");
633 /* ensure self-provides */
634 if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
635 s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
636 s->supplements = repo_fix_supplements(pd->repo, s->provides, s->supplements, pd->freshens);
637 s->conflicts = repo_fix_conflicts(pd->repo, s->conflicts);
640 /* see bugzilla bnc#190163 */
641 flavor = findKernelFlavor(pd, s);
644 char *cflavor = solv_strdup(flavor); /* make pointer safe */
649 /* this is either a kernel package or a kmp */
652 Offset prov = s->provides;
654 while ((pid = pd->repo->idarraydata[prov++]) != 0)
656 const char *depname = 0;
661 prd = GETRELDEP(pool, pid);
662 depname = pool_id2str(pool, prd->name);
666 depname = pool_id2str(pool, pid);
670 if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
673 snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
674 pid = pool_str2id(pool, newdep, 1);
676 pid = pool_rel2id(pool, pid, prd->evr, prd->flags, 1);
679 npr = repo_addid_dep(pd->repo, npr, pid, 0);
687 Offset reqs = s->requires;
689 while ((pid = pd->repo->idarraydata[reqs++]) != 0)
691 const char *depname = 0;
696 prd = GETRELDEP(pool, pid);
697 depname = pool_id2str(pool, prd->name);
701 depname = pool_id2str(pool, pid);
704 if (!strncmp(depname, "kernel(", 7) && !strchr(depname, ':'))
707 snprintf(newdep, sizeof(newdep), "kernel(%s:%s", cflavor, depname + 7);
708 pid = pool_str2id(pool, newdep, 1);
710 pid = pool_rel2id(pool, pid, prd->evr, prd->flags, 1);
712 npr = repo_addid_dep(pd->repo, npr, pid, 0);
721 s->name = pool_str2id(pool, pd->content, 1);
724 s->vendor = pool_str2id(pool, pd->content, 1);
726 case STATE_BUILDTIME:
727 t = atoi (pd->content);
729 repodata_set_num(pd->data, s - pool->solvables, SOLVABLE_BUILDTIME, t);
731 case STATE_UPDATE: /* new version, keeping all other metadata */
732 evr = evr2id(pool, pd,
733 pd->epoch ? pd->evrspace + pd->epoch : 0,
734 pd->version ? pd->evrspace + pd->version : 0,
735 pd->release ? pd->evrspace + pd->release : 0);
740 /* use highest evr */
741 if (!s->evr || pool_evrcmp(pool, s->evr, evr, EVRCMP_COMPARE) <= 0)
750 /* ensure buffer space */
751 if (pd->lcontent + 1 + pd->levrspace > pd->aevrspace)
753 pd->evrspace = (char *)realloc(pd->evrspace, pd->lcontent + 1 + pd->levrspace + 256);
754 pd->aevrspace = pd->lcontent + 1 + pd->levrspace + 256;
756 memcpy(pd->evrspace + pd->levrspace, pd->content, pd->lcontent + 1);
757 if (pd->state == STATE_EPOCH || pd->state == STATE_PEPOCH)
758 pd->epoch = pd->levrspace;
759 else if (pd->state == STATE_VERSION || pd->state == STATE_PVERSION)
760 pd->version = pd->levrspace;
762 pd->release = pd->levrspace;
763 pd->levrspace += pd->lcontent + 1;
767 s->arch = pool_str2id(pool, pd->content, 1);
772 pd->state = pd->sbtab[pd->state];
774 /* printf("back from known %d %d %d\n", pd->state, pd->depth, pd->statedepth); */
785 characterData(void *userData, const XML_Char *s, int len)
787 Parsedata *pd = (Parsedata *)userData;
791 /* check if current nodes content is interesting */
795 /* adapt content buffer */
796 l = pd->lcontent + len + 1;
797 if (l > pd->acontent)
799 pd->content = (char *)realloc(pd->content, l + 256);
800 pd->acontent = l + 256;
802 /* append new content to buffer */
803 c = pd->content + pd->lcontent;
810 /*-------------------------------------------------------------------*/
812 #define BUFF_SIZE 8192
815 * read 'helix' type xml from fp
816 * add packages to pool/repo
821 repo_add_helix(Repo *repo, FILE *fp, int flags)
823 Pool *pool = repo->pool;
828 struct stateswitch *sw;
832 now = solv_timems(0);
833 data = repo_add_repodata(repo, flags);
835 /* prepare parsedata */
836 memset(&pd, 0, sizeof(pd));
837 for (i = 0, sw = stateswitches; sw->from != NUMSTATES; i++, sw++)
839 if (!pd.swtab[sw->from])
840 pd.swtab[sw->from] = sw;
841 pd.sbtab[sw->to] = sw->from;
847 pd.content = (char *)malloc(256); /* must hold all solvable kinds! */
851 pd.evrspace = (char *)malloc(256);
856 /* set up XML parser */
858 parser = XML_ParserCreate(NULL);
859 XML_SetUserData(parser, &pd); /* make parserdata available to XML callbacks */
860 XML_SetElementHandler(parser, startElement, endElement);
861 XML_SetCharacterDataHandler(parser, characterData);
863 /* read/parse XML file */
866 l = fread(buf, 1, sizeof(buf), fp);
867 if (XML_Parse(parser, buf, l, l == 0) == XML_STATUS_ERROR)
869 pd.ret = pool_error(pool, -1, "%s at line %u", XML_ErrorString(XML_GetErrorCode(parser)), (unsigned int)XML_GetCurrentLineNumber(parser));
875 XML_ParserFree(parser);
879 if (!(flags & REPO_NO_INTERNALIZE))
880 repodata_internalize(data);
881 POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_helix took %d ms\n", solv_timems(now));
882 POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
883 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)));