/* * repo_comps.c * * Parses RedHat comps format * * Copyright (c) 2012, Novell Inc. * * This program is licensed under the BSD license, read LICENSE.BSD * for further information */ #include #include #include #include #include #include #include #include "pool.h" #include "repo.h" #include "util.h" #include "solv_xmlparser.h" #define DISABLE_SPLIT #include "tools_util.h" #include "repo_comps.h" /* * TODO: * * what's the difference between group/category? * * maybe handle REL_COND in solver recommends handling? */ enum state { STATE_START, STATE_COMPS, STATE_GROUP, STATE_ID, STATE_NAME, STATE_DESCRIPTION, STATE_DISPLAY_ORDER, STATE_DEFAULT, STATE_LANGONLY, STATE_LANG_ONLY, STATE_USERVISIBLE, STATE_PACKAGELIST, STATE_PACKAGEREQ, STATE_CATEGORY, STATE_CID, STATE_CNAME, STATE_CDESCRIPTION, STATE_CDISPLAY_ORDER, STATE_GROUPLIST, STATE_GROUPID, NUMSTATES }; static struct solv_xmlparser_element stateswitches[] = { { STATE_START, "comps", STATE_COMPS, 0 }, { STATE_COMPS, "group", STATE_GROUP, 0 }, { STATE_COMPS, "category", STATE_CATEGORY, 0 }, { STATE_GROUP, "id", STATE_ID, 1 }, { STATE_GROUP, "name", STATE_NAME, 1 }, { STATE_GROUP, "description", STATE_DESCRIPTION, 1 }, { STATE_GROUP, "uservisible", STATE_USERVISIBLE, 1 }, { STATE_GROUP, "display_order", STATE_DISPLAY_ORDER, 1 }, { STATE_GROUP, "default", STATE_DEFAULT, 1 }, { STATE_GROUP, "langonly", STATE_LANGONLY, 1 }, { STATE_GROUP, "lang_only", STATE_LANG_ONLY, 1 }, { STATE_GROUP, "packagelist", STATE_PACKAGELIST, 0 }, { STATE_PACKAGELIST, "packagereq", STATE_PACKAGEREQ, 1 }, { STATE_CATEGORY, "id", STATE_ID, 1 }, { STATE_CATEGORY, "name", STATE_NAME, 1 }, { STATE_CATEGORY, "description", STATE_DESCRIPTION, 1 }, { STATE_CATEGORY , "grouplist", STATE_GROUPLIST, 0 }, { STATE_CATEGORY , "display_order", STATE_DISPLAY_ORDER, 1 }, { STATE_GROUPLIST, "groupid", STATE_GROUPID, 1 }, { NUMSTATES } }; struct parsedata { Pool *pool; Repo *repo; Repodata *data; const char *filename; const char *basename; struct solv_xmlparser xmlp; struct joindata jd; const char *tmplang; Id reqtype; Id condreq; Solvable *solvable; const char *kind; int isdefault; int isvisible; Id handle; }; #define COMPS_DEFAULT_ISVISIBLE 1 #define COMPS_DEFAULT_ISDEFAULT 0 /* Return true if "true", false if "false", default_value otherwise */ static int parse_boolean(char *content, int default_value) { if (!strcmp(content, "true")) return 1; if (!strcmp(content, "false")) return 0; return default_value; } static void startElement(struct solv_xmlparser *xmlp, int state, const char *name, const char **atts) { struct parsedata *pd = xmlp->userdata; Pool *pool = pd->pool; Solvable *s; switch(state) { case STATE_GROUP: case STATE_CATEGORY: s = pd->solvable = pool_id2solvable(pool, repo_add_solvable(pd->repo)); pd->handle = s - pool->solvables; pd->kind = state == STATE_GROUP ? "group" : "category"; pd->isvisible = COMPS_DEFAULT_ISVISIBLE; pd->isdefault = COMPS_DEFAULT_ISDEFAULT; break; case STATE_NAME: case STATE_CNAME: case STATE_DESCRIPTION: case STATE_CDESCRIPTION: pd->tmplang = join_dup(&pd->jd, solv_xmlparser_find_attr("xml:lang", atts)); break; case STATE_PACKAGEREQ: { const char *type = solv_xmlparser_find_attr("type", atts); pd->condreq = 0; pd->reqtype = SOLVABLE_RECOMMENDS; if (type && !strcmp(type, "conditional")) { const char *requires = solv_xmlparser_find_attr("requires", atts); if (requires && *requires) pd->condreq = pool_str2id(pool, requires, 1); } else if (type && !strcmp(type, "mandatory")) pd->reqtype = SOLVABLE_REQUIRES; else if (type && !strcmp(type, "optional")) pd->reqtype = SOLVABLE_SUGGESTS; break; } default: break; } } static void endElement(struct solv_xmlparser *xmlp, int state, char *content) { struct parsedata *pd = xmlp->userdata; Solvable *s = pd->solvable; Id id; switch (state) { case STATE_GROUP: case STATE_CATEGORY: if (!s->arch) s->arch = ARCH_NOARCH; if (!s->evr) s->evr = ID_EMPTY; if (s->name && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC) s->provides = repo_addid_dep(pd->repo, s->provides, pool_rel2id(pd->pool, s->name, s->evr, REL_EQ, 1), 0); if (pd->isvisible) repodata_set_void(pd->data, pd->handle, SOLVABLE_ISVISIBLE); if (pd->isdefault) repodata_set_void(pd->data, pd->handle, SOLVABLE_ISDEFAULT); pd->solvable = 0; break; case STATE_ID: s->name = pool_str2id(pd->pool, join2(&pd->jd, pd->kind, ":", content), 1); break; case STATE_NAME: repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_SUMMARY, pd->tmplang, 1), content); break; case STATE_DESCRIPTION: repodata_set_str(pd->data, pd->handle, pool_id2langid(pd->pool, SOLVABLE_DESCRIPTION, pd->tmplang, 1), content); break; case STATE_PACKAGEREQ: id = pool_str2id(pd->pool, content, 1); if (pd->condreq) id = pool_rel2id(pd->pool, id, pd->condreq, REL_COND, 1); repo_add_idarray(pd->repo, pd->handle, pd->reqtype, id); break; case STATE_GROUPID: id = pool_str2id(pd->pool, join2(&pd->jd, "group", ":", content), 1); s->requires = repo_addid_dep(pd->repo, s->requires, id, 0); break; case STATE_USERVISIBLE: pd->isvisible = parse_boolean(content, COMPS_DEFAULT_ISVISIBLE); break; case STATE_DEFAULT: pd->isdefault = parse_boolean(content, COMPS_DEFAULT_ISDEFAULT); break; case STATE_LANG_ONLY: repodata_set_str(pd->data, pd->handle, SOLVABLE_LANGONLY, content); break; case STATE_LANGONLY: repodata_set_str(pd->data, pd->handle, SOLVABLE_LANGONLY, content); break; case STATE_DISPLAY_ORDER: repodata_set_str(pd->data, pd->handle, SOLVABLE_ORDER, content); break; default: break; } } int repo_add_comps(Repo *repo, FILE *fp, int flags) { Repodata *data; struct parsedata pd; data = repo_add_repodata(repo, flags); memset(&pd, 0, sizeof(pd)); pd.repo = repo; pd.pool = repo->pool; pd.data = data; solv_xmlparser_init(&pd.xmlp, stateswitches, &pd, startElement, endElement); if (solv_xmlparser_parse(&pd.xmlp, fp) != SOLV_XMLPARSER_OK) pool_debug(pd.pool, SOLV_ERROR, "repo_comps: %s at line %u:%u\n", pd.xmlp.errstr, pd.xmlp.line, pd.xmlp.column); solv_xmlparser_free(&pd.xmlp); join_freemem(&pd.jd); if (!(flags & REPO_NO_INTERNALIZE)) repodata_internalize(data); return 0; } /* EOF */