/
niceSOFT
/
createrepo_c
Обзор
Документация
Войти
/
niceSOFT
/
createrepo_c
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/xml_parser_primary.c
795 строк
27 KB
Aleš Matěj
Internally use package_internal.h to access cr_package fields
26 июн 2026, 17:40
26 июн 2026, 17:40
3a39893
Код
Авторство
О чём код?
/* createrepo_c - Library of routines for manipulation with repodata * Copyright (C) 2013 Tomas Mlcoch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ #include <glib.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <errno.h> #include "xml_parser_internal.h" #include "xml_parser.h" #include "error.h" #include "package_internal.h" #include "misc.h" #define ERR_DOMAIN CREATEREPO_C_ERROR #define ERR_CODE_XML CRE_BADXMLPRIMARY typedef enum { STATE_START, STATE_METADATA, STATE_PACKAGE, STATE_NAME, STATE_ARCH, STATE_VERSION, STATE_CHECKSUM, STATE_SUMMARY, STATE_DESCRIPTION, STATE_PACKAGER, STATE_URL, STATE_TIME, STATE_SIZE, STATE_LOCATION, STATE_FORMAT, STATE_RPM_LICENSE, STATE_RPM_VENDOR, STATE_RPM_GROUP, STATE_RPM_BUILDHOST, STATE_RPM_SOURCERPM, STATE_RPM_HEADER_RANGE, STATE_RPM_PROVIDES, STATE_RPM_ENTRY_PROVIDES, STATE_RPM_REQUIRES, STATE_RPM_ENTRY_REQUIRES, STATE_RPM_CONFLICTS, STATE_RPM_ENTRY_CONFLICTS, STATE_RPM_OBSOLETES, STATE_RPM_ENTRY_OBSOLETES, STATE_RPM_SUGGESTS, STATE_RPM_ENTRY_SUGGESTS, STATE_RPM_ENHANCES, STATE_RPM_ENTRY_ENHANCES, STATE_RPM_RECOMMENDS, STATE_RPM_ENTRY_RECOMMENDS, STATE_RPM_SUPPLEMENTS, STATE_RPM_ENTRY_SUPPLEMENTS, STATE_FILE, NUMSTATES, } cr_PriState; /* NOTE: Same states in the first column must be together!!! * Performance tip: More frequent elements should be listed * first in its group (eg: element "package" (STATE_PACKAGE) * has a "file" element listed first, because it is more frequent * than other elements). */ static cr_StatesSwitch stateswitches[] = { { STATE_START, "metadata", STATE_METADATA, 0 }, { STATE_METADATA, "package", STATE_PACKAGE, 0 }, { STATE_PACKAGE, "name", STATE_NAME, 1 }, { STATE_PACKAGE, "arch", STATE_ARCH, 1 }, { STATE_PACKAGE, "version", STATE_VERSION, 0 }, { STATE_PACKAGE, "checksum", STATE_CHECKSUM, 1 }, { STATE_PACKAGE, "summary", STATE_SUMMARY, 1 }, { STATE_PACKAGE, "description", STATE_DESCRIPTION, 1 }, { STATE_PACKAGE, "packager", STATE_PACKAGER, 1 }, { STATE_PACKAGE, "url", STATE_URL, 1 }, { STATE_PACKAGE, "time", STATE_TIME, 0 }, { STATE_PACKAGE, "size", STATE_SIZE, 0 }, { STATE_PACKAGE, "location", STATE_LOCATION, 0 }, { STATE_PACKAGE, "format", STATE_FORMAT, 0 }, { STATE_FORMAT, "file", STATE_FILE, 1 }, { STATE_FORMAT, "rpm:license", STATE_RPM_LICENSE, 1 }, { STATE_FORMAT, "rpm:vendor", STATE_RPM_VENDOR, 1 }, { STATE_FORMAT, "rpm:group", STATE_RPM_GROUP, 1 }, { STATE_FORMAT, "rpm:buildhost", STATE_RPM_BUILDHOST, 1 }, { STATE_FORMAT, "rpm:sourcerpm", STATE_RPM_SOURCERPM, 1 }, { STATE_FORMAT, "rpm:header-range", STATE_RPM_HEADER_RANGE, 0 }, { STATE_FORMAT, "rpm:provides", STATE_RPM_PROVIDES, 0 }, { STATE_FORMAT, "rpm:requires", STATE_RPM_REQUIRES, 0 }, { STATE_FORMAT, "rpm:conflicts", STATE_RPM_CONFLICTS, 0 }, { STATE_FORMAT, "rpm:obsoletes", STATE_RPM_OBSOLETES, 0 }, { STATE_FORMAT, "rpm:suggests", STATE_RPM_SUGGESTS, 0 }, { STATE_FORMAT, "rpm:enhances", STATE_RPM_ENHANCES, 0 }, { STATE_FORMAT, "rpm:recommends", STATE_RPM_RECOMMENDS, 0 }, { STATE_FORMAT, "rpm:supplements", STATE_RPM_SUPPLEMENTS, 0 }, { STATE_RPM_PROVIDES, "rpm:entry", STATE_RPM_ENTRY_PROVIDES, 0 }, { STATE_RPM_REQUIRES, "rpm:entry", STATE_RPM_ENTRY_REQUIRES, 0 }, { STATE_RPM_CONFLICTS, "rpm:entry", STATE_RPM_ENTRY_CONFLICTS, 0 }, { STATE_RPM_OBSOLETES, "rpm:entry", STATE_RPM_ENTRY_OBSOLETES, 0 }, { STATE_RPM_SUGGESTS, "rpm:entry", STATE_RPM_ENTRY_SUGGESTS, 0 }, { STATE_RPM_ENHANCES, "rpm:entry", STATE_RPM_ENTRY_ENHANCES, 0 }, { STATE_RPM_RECOMMENDS, "rpm:entry", STATE_RPM_ENTRY_RECOMMENDS, 0 }, { STATE_RPM_SUPPLEMENTS,"rpm:entry", STATE_RPM_ENTRY_SUPPLEMENTS, 0 }, { NUMSTATES, NULL, NUMSTATES, 0 }, }; static void XMLCALL cr_start_handler(void *pdata, const xmlChar *element, const xmlChar **attr) { GError *tmp_err = NULL; cr_ParserData *pd = pdata; cr_StatesSwitch *sw; if (pd->err) return; // There was an error -> do nothing if (pd->depth != pd->statedepth) { // We are inside of unknown element pd->depth++; return; } pd->depth++; if (!pd->swtab[pd->state]) { // Current element should not have any sub elements return; } if (!pd->pkg && pd->state != STATE_METADATA && pd->state != STATE_START) return; // Do not parse current package tag and its content // Find current state by its name for (sw = pd->swtab[pd->state]; sw->from == pd->state; sw++) if (!strcmp((char *) element, sw->ename)) break; if (sw->from != pd->state) { // No state for current element (unknown element) cr_xml_parser_warning(pd, CR_XML_WARNING_UNKNOWNTAG, "Unknown element \"%s\"", element); return; } gboolean free_attr = FALSE; attr = unescape_ampersand_from_values(attr, &free_attr); // Update parser data pd->state = sw->to; pd->docontent = sw->docontent; pd->statedepth = pd->depth; pd->lcontent = 0; pd->content[0] = '\0'; const char *val; switch(pd->state) { case STATE_START: break; case STATE_METADATA: pd->main_tag_found = TRUE; break; case STATE_PACKAGE: assert(!pd->pkg); val = cr_find_attr("type", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"type\" of a package element"); // Get package object to store current package or NULL if // current XML package element should be skipped/ignored. if (pd->newpkgcb(&pd->pkg, val, NULL, NULL, pd->newpkgcb_data, &tmp_err)) { if (tmp_err) g_propagate_prefixed_error(&pd->err, tmp_err, "Parsing interrupted: "); else g_set_error(&pd->err, ERR_DOMAIN, CRE_CBINTERRUPTED, "Parsing interrupted"); break; } else { // If callback return CRE_OK but it simultaneously set // the tmp_err then it's a programming error. assert(tmp_err == NULL); } break; case STATE_NAME: case STATE_ARCH: break; case STATE_VERSION: assert(pd->pkg); // Version strings insert only if them don't already exists // They could be already filled by filelists or other parser. if (!pd->pkg->epoch) pd->pkg->epoch = cr_safe_string_chunk_insert(pd->pkg->chunk, cr_find_attr("epoch", attr)); if (!pd->pkg->version) pd->pkg->version = cr_safe_string_chunk_insert(pd->pkg->chunk, cr_find_attr("ver", attr)); if (!pd->pkg->release) pd->pkg->release = cr_safe_string_chunk_insert(pd->pkg->chunk, cr_find_attr("rel", attr)); break; case STATE_CHECKSUM: assert(pd->pkg); val = cr_find_attr("type", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"type\" of a checksum element"); else pd->pkg->checksum_type = g_string_chunk_insert(pd->pkg->chunk, val); break; case STATE_SUMMARY: case STATE_DESCRIPTION: case STATE_PACKAGER: case STATE_URL: break; case STATE_TIME: assert(pd->pkg); val = cr_find_attr("file", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"file\" of a time element"); else pd->pkg->time_file = cr_xml_parser_strtoll(pd, val, 10); val = cr_find_attr("build", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"build\" of a time element"); else pd->pkg->time_build = cr_xml_parser_strtoll(pd, val, 10); break; case STATE_SIZE: assert(pd->pkg); val = cr_find_attr("package", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"package\" of a size element"); else pd->pkg->size_package = cr_xml_parser_strtoll(pd, val, 10); val = cr_find_attr("installed", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"installed\" of a size element"); else pd->pkg->size_installed = cr_xml_parser_strtoll(pd, val, 10); val = cr_find_attr("archive", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"archive\" of a size element"); else pd->pkg->size_archive = cr_xml_parser_strtoll(pd, val, 10); break; case STATE_LOCATION: assert(pd->pkg); val = cr_find_attr("href", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"href\" of a location element"); else pd->pkg->location_href = g_string_chunk_insert(pd->pkg->chunk, val); val = cr_find_attr("xml:base", attr); if (val) pd->pkg->location_base = g_string_chunk_insert(pd->pkg->chunk, val); break; case STATE_FORMAT: case STATE_RPM_LICENSE: case STATE_RPM_VENDOR: case STATE_RPM_GROUP: case STATE_RPM_BUILDHOST: case STATE_RPM_SOURCERPM: break; case STATE_RPM_HEADER_RANGE: assert(pd->pkg); val = cr_find_attr("start", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"start\" of a header-range element"); else pd->pkg->rpm_header_start = cr_xml_parser_strtoll(pd, val, 10); val = cr_find_attr("end", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"end\" of a time element"); else pd->pkg->rpm_header_end = cr_xml_parser_strtoll(pd, val, 10); break; case STATE_RPM_PROVIDES: case STATE_RPM_REQUIRES: case STATE_RPM_CONFLICTS: case STATE_RPM_OBSOLETES: case STATE_RPM_SUGGESTS: case STATE_RPM_ENHANCES: case STATE_RPM_RECOMMENDS: case STATE_RPM_SUPPLEMENTS: break; case STATE_RPM_ENTRY_PROVIDES: case STATE_RPM_ENTRY_REQUIRES: case STATE_RPM_ENTRY_CONFLICTS: case STATE_RPM_ENTRY_OBSOLETES: case STATE_RPM_ENTRY_SUGGESTS: case STATE_RPM_ENTRY_ENHANCES: case STATE_RPM_ENTRY_RECOMMENDS: case STATE_RPM_ENTRY_SUPPLEMENTS: { assert(pd->pkg); cr_Dependency *dep = cr_dependency_new(); val = cr_find_attr("name", attr); if (!val) cr_xml_parser_warning(pd, CR_XML_WARNING_MISSINGATTR, "Missing attribute \"name\" of an entry element"); else dep->name = g_string_chunk_insert(pd->pkg->chunk, val); // Rest of attrs is optional val = cr_find_attr("flags", attr); if (val) dep->flags = g_string_chunk_insert(pd->pkg->chunk, val); val = cr_find_attr("epoch", attr); if (val) dep->epoch = g_string_chunk_insert(pd->pkg->chunk, val); val = cr_find_attr("ver", attr); if (val) dep->version = g_string_chunk_insert(pd->pkg->chunk, val); val = cr_find_attr("rel", attr); if (val) dep->release = g_string_chunk_insert(pd->pkg->chunk, val); val = cr_find_attr("pre", attr); if (val) { if (!strcmp(val, "0") || !strcmp(val, "FALSE") || !strcmp(val, "false") || !strcmp(val, "False")) dep->pre = FALSE; else dep->pre = TRUE; } switch (pd->state) { case STATE_RPM_ENTRY_PROVIDES: pd->pkg->provides = g_slist_prepend(pd->pkg->provides, dep); break; case STATE_RPM_ENTRY_REQUIRES: pd->pkg->requires = g_slist_prepend(pd->pkg->requires, dep); break; case STATE_RPM_ENTRY_CONFLICTS: pd->pkg->conflicts = g_slist_prepend(pd->pkg->conflicts, dep); break; case STATE_RPM_ENTRY_OBSOLETES: pd->pkg->obsoletes = g_slist_prepend(pd->pkg->obsoletes, dep); break; case STATE_RPM_ENTRY_SUGGESTS: pd->pkg->suggests = g_slist_prepend(pd->pkg->suggests, dep); break; case STATE_RPM_ENTRY_ENHANCES: pd->pkg->enhances = g_slist_prepend(pd->pkg->enhances, dep); break; case STATE_RPM_ENTRY_RECOMMENDS: pd->pkg->recommends = g_slist_prepend(pd->pkg->recommends, dep); break; case STATE_RPM_ENTRY_SUPPLEMENTS: pd->pkg->supplements = g_slist_prepend(pd->pkg->supplements, dep); break; default: assert(0); } break; } case STATE_FILE: assert(pd->pkg); if (!pd->do_files) break; val = cr_find_attr("type", attr); pd->last_file_type = FILE_FILE; if (val) { if (!strcmp(val, "dir")) pd->last_file_type = FILE_DIR; else if (!strcmp(val, "ghost")) pd->last_file_type = FILE_GHOST; else cr_xml_parser_warning(pd, CR_XML_WARNING_UNKNOWNVAL, "Unknown file type \"%s\"", val); } break; default: break; } if (free_attr) { g_strfreev((char **)attr); } } static void XMLCALL cr_end_handler(void *pdata, G_GNUC_UNUSED const xmlChar *element) { cr_ParserData *pd = pdata; GError *tmp_err = NULL; unsigned int state = pd->state; if (pd->err) return; // There was an error -> do nothing if (pd->depth != pd->statedepth) { // Back from the unknown state pd->depth--; return; } pd->depth--; pd->statedepth--; pd->state = pd->sbtab[pd->state]; pd->docontent = 0; switch (state) { case STATE_START: case STATE_METADATA: break; case STATE_PACKAGE: if (!pd->pkg) return; if (!pd->pkg->pkgId) { // Package without a pkgid attr is error g_set_error(&pd->err, ERR_DOMAIN, ERR_CODE_XML, "Package without pkgid (checksum)!"); break; } if (pd->pkg->pkgId[0] == '\0') { // Package without a pkgid attr is error g_set_error(&pd->err, ERR_DOMAIN, ERR_CODE_XML, "Package with empty pkgid (checksum)!"); break; } if (pd->do_files) // Reverse order of files pd->pkg->files = g_slist_reverse(pd->pkg->files); if (pd->pkgcb && pd->pkgcb(pd->pkg, pd->pkgcb_data, &tmp_err)) { if (tmp_err) g_propagate_prefixed_error(&pd->err, tmp_err, "Parsing interrupted: "); else g_set_error(&pd->err, ERR_DOMAIN, CRE_CBINTERRUPTED, "Parsing interrupted"); } else { // If callback return CRE_OK but it simultaneously set // the tmp_err then it's a programming error. assert(tmp_err == NULL); } pd->pkg = NULL; break; case STATE_NAME: assert(pd->pkg); if (!pd->pkg->name) // name could be already filled by filelists or other xml parser pd->pkg->name = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_ARCH: assert(pd->pkg); if (!pd->pkg->arch) // arch could be already filled by filelists or other xml parser pd->pkg->arch = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_CHECKSUM: assert(pd->pkg); if (!pd->pkg->pkgId) // pkgId could be already filled by filelists or other xml parser pd->pkg->pkgId = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_SUMMARY: assert(pd->pkg); pd->pkg->summary = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_DESCRIPTION: assert(pd->pkg); pd->pkg->description = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_PACKAGER: assert(pd->pkg); pd->pkg->rpm_packager = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_URL: assert(pd->pkg); pd->pkg->url = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_RPM_LICENSE: assert(pd->pkg); pd->pkg->rpm_license = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_RPM_VENDOR: assert(pd->pkg); pd->pkg->rpm_vendor = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_RPM_GROUP: assert(pd->pkg); pd->pkg->rpm_group = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_RPM_BUILDHOST: assert(pd->pkg); pd->pkg->rpm_buildhost = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_RPM_SOURCERPM: assert(pd->pkg); pd->pkg->rpm_sourcerpm = cr_safe_string_chunk_insert_null(pd->pkg->chunk, pd->content); break; case STATE_RPM_PROVIDES: pd->pkg->provides = g_slist_reverse(pd->pkg->provides); break; case STATE_RPM_REQUIRES: pd->pkg->requires = g_slist_reverse(pd->pkg->requires); break; case STATE_RPM_CONFLICTS: pd->pkg->conflicts = g_slist_reverse(pd->pkg->conflicts); break; case STATE_RPM_OBSOLETES: pd->pkg->obsoletes = g_slist_reverse(pd->pkg->obsoletes); break; case STATE_RPM_SUGGESTS: pd->pkg->suggests = g_slist_reverse(pd->pkg->suggests); break; case STATE_RPM_ENHANCES: pd->pkg->enhances = g_slist_reverse(pd->pkg->enhances); break; case STATE_RPM_RECOMMENDS: pd->pkg->recommends = g_slist_reverse(pd->pkg->recommends); break; case STATE_RPM_SUPPLEMENTS: pd->pkg->supplements = g_slist_reverse(pd->pkg->supplements); break; case STATE_FILE: { assert(pd->pkg); if (!pd->do_files) break; if (!pd->content) break; cr_PackageFile *pkg_file = cr_package_file_new(); pkg_file->name = cr_safe_string_chunk_insert(pd->pkg->chunk, cr_get_filename(pd->content)); if (!pkg_file->name) { g_set_error(&pd->err, ERR_DOMAIN, ERR_CODE_XML, "Invalid <file> element: %s", pd->content); g_free(pkg_file); break; } pd->content[pd->lcontent - strlen(pkg_file->name)] = '\0'; pkg_file->path = cr_safe_string_chunk_insert_const(pd->pkg->chunk, pd->content); switch (pd->last_file_type) { case FILE_FILE: pkg_file->type = NULL; break; // NULL => "file" case FILE_DIR: pkg_file->type = "dir"; break; case FILE_GHOST: pkg_file->type = "ghost"; break; default: assert(0); // Should not happend } pd->pkg->files = g_slist_prepend(pd->pkg->files, pkg_file); break; } default: break; } } cr_ParserData * primary_parser_data_new(cr_XmlParserNewPkgCb newpkgcb, void *newpkgcb_data, cr_XmlParserPkgCb pkgcb, void *pkgcb_data, cr_XmlParserWarningCb warningcb, void *warningcb_data, int do_files) { cr_ParserData *pd; assert(newpkgcb || pkgcb); if (!newpkgcb) // Use default newpkgcb newpkgcb = cr_newpkgcb; // Init xmlSAXHandler sax; memset(&sax, 0, sizeof(sax)); sax.startElement = cr_start_handler; sax.endElement = cr_end_handler; sax.characters = cr_char_handler; pd = cr_xml_parser_data(NUMSTATES); pd->parser = xmlCreatePushParserCtxt(&sax, pd, NULL, 0, NULL); pd->state = STATE_START; pd->newpkgcb_data = newpkgcb_data; pd->newpkgcb = newpkgcb; pd->pkgcb_data = pkgcb_data; pd->pkgcb = pkgcb; pd->do_files = do_files; pd->warningcb = warningcb; pd->warningcb_data = warningcb_data; for (cr_StatesSwitch *sw = stateswitches; sw->from != NUMSTATES; sw++) { if (!pd->swtab[sw->from]) pd->swtab[sw->from] = sw; pd->sbtab[sw->to] = sw->from; } return pd; } int cr_xml_parse_primary_internal(const char *target, cr_XmlParserNewPkgCb newpkgcb, void *newpkgcb_data, cr_XmlParserPkgCb pkgcb, void *pkgcb_data, cr_XmlParserWarningCb warningcb, void *warningcb_data, int do_files, int (*parser_func)(xmlParserCtxtPtr, cr_ParserData *, const char *, GError**), GError **err) { int ret = CRE_OK; GError *tmp_err = NULL; assert(target); assert(!err || *err == NULL); cr_ParserData *pd; pd = primary_parser_data_new(newpkgcb, newpkgcb_data, pkgcb, pkgcb_data, warningcb, warningcb_data, do_files); // Parsing ret = parser_func(pd->parser, pd, target, &tmp_err); if (tmp_err) g_propagate_error(err, tmp_err); // Warning if file was probably a different type than expected if (!pd->main_tag_found && ret == CRE_OK) cr_xml_parser_warning(pd, CR_XML_WARNING_BADMDTYPE, "The target doesn't contain the expected element " "\"<metadata>\" - The target probably isn't " "a valid primary xml"); // Clean up if (ret != CRE_OK && newpkgcb == cr_newpkgcb) { // Prevent memory leak when the parsing is interrupted by an error. // If a new package object was created by the cr_newpkgcb then // is obvious that there is no other reference to the package // except of the parser reference in pd->pkg. // If a caller supplied its own newpkgcb, then the freeing // of the currently parsed package is the caller responsibility. cr_package_free(pd->pkg); } cr_xml_parser_data_free(pd); return ret; } int cr_xml_parse_primary(const char *path, cr_XmlParserNewPkgCb newpkgcb, void *newpkgcb_data, cr_XmlParserPkgCb pkgcb, void *pkgcb_data, cr_XmlParserWarningCb warningcb, void *warningcb_data, int do_files, GError **err) { return cr_xml_parse_primary_internal(path, newpkgcb, newpkgcb_data, pkgcb, pkgcb_data, warningcb, warningcb_data, do_files, &cr_xml_parser_generic, err); } int cr_xml_parse_primary_snippet(const char *xml_string, cr_XmlParserNewPkgCb newpkgcb, void *newpkgcb_data, cr_XmlParserPkgCb pkgcb, void *pkgcb_data, cr_XmlParserWarningCb warningcb, void *warningcb_data, int do_files, GError **err) { gchar* wrapped_xml_string = g_strconcat("<metadata>", xml_string, "</metadata>", NULL); int ret = cr_xml_parse_primary_internal(wrapped_xml_string, newpkgcb, newpkgcb_data, pkgcb, pkgcb_data, warningcb, warningcb_data, do_files, &cr_xml_parser_generic_from_string, err); g_free(wrapped_xml_string); return ret; }