parser xml BUGFIX full xml context backup
... including opened elements and namespaces.
Fixes cesnet/netopeer2#930
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 570d6b3..a827ab7 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -330,10 +330,7 @@
lydxml_data_check_opaq(struct lyd_xml_ctx *lydctx, const struct lysc_node **snode)
{
LY_ERR ret = LY_SUCCESS;
- enum LYXML_PARSER_STATUS prev_status;
- const char *prev_current, *pname, *pprefix;
- size_t pprefix_len, pname_len;
- struct lyxml_ctx *xmlctx = lydctx->xmlctx;
+ struct lyxml_ctx *xmlctx = lydctx->xmlctx, pxmlctx;
if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
/* only checks specific to opaque nodes */
@@ -345,17 +342,10 @@
return LY_SUCCESS;
}
+ assert(xmlctx->elements.count);
+
/* backup parser */
- prev_status = xmlctx->status;
- pprefix = xmlctx->prefix;
- pprefix_len = xmlctx->prefix_len;
- pname = xmlctx->name;
- pname_len = xmlctx->name_len;
- prev_current = xmlctx->in->current;
- if ((xmlctx->status == LYXML_ELEM_CONTENT) && xmlctx->dynamic) {
- /* it was backed up, do not free */
- xmlctx->dynamic = 0;
- }
+ LY_CHECK_RET(lyxml_ctx_backup(xmlctx, &pxmlctx));
/* skip attributes */
while (xmlctx->status == LYXML_ATTRIBUTE) {
@@ -386,16 +376,7 @@
restore:
/* restore parser */
- if (xmlctx->dynamic) {
- free((char *)xmlctx->value);
- }
- xmlctx->status = prev_status;
- xmlctx->prefix = pprefix;
- xmlctx->prefix_len = pprefix_len;
- xmlctx->name = pname;
- xmlctx->name_len = pname_len;
- xmlctx->in->current = prev_current;
-
+ lyxml_ctx_restore(xmlctx, &pxmlctx);
return ret;
}
@@ -537,6 +518,10 @@
LY_CHECK_GOTO(ret, error);
}
+ /* get NS again, it may have been backed up and restored */
+ ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
+ assert(ns);
+
/* create node */
ret = lyd_create_opaq(ctx, name, name_len, prefix, prefix_len, ns->uri, strlen(ns->uri), xmlctx->value,
xmlctx->value_len, &xmlctx->dynamic, format, val_prefix_data, LYD_HINT_DATA, &node);
diff --git a/src/xml.c b/src/xml.c
index 1294b37..91b50bb 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Generic XML parser implementation for libyang
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -1039,11 +1039,30 @@
return ret;
}
+/**
+ * @brief Free all namespaces in XML context.
+ *
+ * @param[in] xmlctx XML context to use.
+ */
+static void
+lyxml_ns_rm_all(struct lyxml_ctx *xmlctx)
+{
+ struct lyxml_ns *ns;
+ uint32_t i;
+
+ for (i = 0; i < xmlctx->ns.count; ++i) {
+ ns = xmlctx->ns.objs[i];
+
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
+ }
+ ly_set_erase(&xmlctx->ns, NULL);
+}
+
void
lyxml_ctx_free(struct lyxml_ctx *xmlctx)
{
- uint32_t u;
-
if (!xmlctx) {
return;
}
@@ -1054,16 +1073,111 @@
free((char *)xmlctx->value);
}
ly_set_erase(&xmlctx->elements, free);
- for (u = xmlctx->ns.count - 1; u + 1 > 0; --u) {
- /* remove the ns structure */
- free(((struct lyxml_ns *)xmlctx->ns.objs[u])->prefix);
- free(((struct lyxml_ns *)xmlctx->ns.objs[u])->uri);
- free(xmlctx->ns.objs[u]);
- }
- ly_set_erase(&xmlctx->ns, NULL);
+ lyxml_ns_rm_all(xmlctx);
free(xmlctx);
}
+/**
+ * @brief Duplicate an XML element.
+ *
+ * @param[in] elem Element to duplicate.
+ * @return Element duplicate.
+ * @return NULL on error.
+ */
+static struct lyxml_elem *
+lyxml_elem_dup(const struct lyxml_elem *elem)
+{
+ struct lyxml_elem *dup;
+
+ dup = malloc(sizeof *dup);
+ LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
+
+ memcpy(dup, elem, sizeof *dup);
+
+ return dup;
+}
+
+/**
+ * @brief Duplicate an XML namespace.
+ *
+ * @param[in] ns Namespace to duplicate.
+ * @return Namespace duplicate.
+ * @return NULL on error.
+ */
+static struct lyxml_ns *
+lyxml_ns_dup(const struct lyxml_ns *ns)
+{
+ struct lyxml_ns *dup;
+
+ dup = malloc(sizeof *dup);
+ LY_CHECK_ERR_RET(!dup, LOGMEM(NULL), NULL);
+
+ if (ns->prefix) {
+ dup->prefix = strdup(ns->prefix);
+ LY_CHECK_ERR_RET(!dup->prefix, LOGMEM(NULL); free(dup), NULL);
+ } else {
+ dup->prefix = NULL;
+ }
+ dup->uri = strdup(ns->uri);
+ LY_CHECK_ERR_RET(!dup->uri, LOGMEM(NULL); free(dup->prefix); free(dup), NULL);
+ dup->depth = ns->depth;
+
+ return dup;
+}
+
+LY_ERR
+lyxml_ctx_backup(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
+{
+ uint32_t i;
+
+ /* first make shallow copy */
+ memcpy(backup, xmlctx, sizeof *backup);
+
+ if ((xmlctx->status == LYXML_ELEM_CONTENT) && xmlctx->dynamic) {
+ /* it was backed up, do not free */
+ xmlctx->dynamic = 0;
+ }
+
+ /* backup in current pointer only */
+ backup->in = (void *)xmlctx->in->current;
+
+ /* duplicate elements */
+ backup->elements.objs = malloc(xmlctx->elements.size * sizeof(struct lyxml_elem));
+ for (i = 0; i < xmlctx->elements.count; ++i) {
+ backup->elements.objs[i] = lyxml_elem_dup(xmlctx->elements.objs[i]);
+ }
+
+ /* duplicate ns */
+ backup->ns.objs = malloc(xmlctx->ns.size * sizeof(struct lyxml_ns));
+ for (i = 0; i < xmlctx->ns.count; ++i) {
+ backup->ns.objs[i] = lyxml_ns_dup(xmlctx->ns.objs[i]);
+ }
+
+ return LY_SUCCESS;
+}
+
+void
+lyxml_ctx_restore(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup)
+{
+ if (((xmlctx->status == LYXML_ELEM_CONTENT) || (xmlctx->status == LYXML_ATTR_CONTENT)) && xmlctx->dynamic) {
+ /* free dynamic value */
+ free((char *)xmlctx->value);
+ }
+
+ /* free elements */
+ ly_set_erase(&xmlctx->elements, free);
+
+ /* free ns */
+ lyxml_ns_rm_all(xmlctx);
+
+ /* restore in current pointer */
+ xmlctx->in->current = (void *)backup->in;
+ backup->in = xmlctx->in;
+
+ /* restore backup */
+ memcpy(xmlctx, backup, sizeof *xmlctx);
+}
+
LY_ERR
lyxml_dump_text(struct ly_out *out, const char *text, ly_bool attribute)
{
diff --git a/src/xml.h b/src/xml.h
index 518c445..1261e31 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -57,8 +57,8 @@
/* element tag identifier for matching opening and closing tags */
struct lyxml_elem {
- const char *prefix;
- const char *name;
+ const char *prefix; /**< only pointer, not in dictionary */
+ const char *name; /**< only pointer, not in dictionary */
size_t prefix_len;
size_t name_len;
};
@@ -162,6 +162,23 @@
void lyxml_ctx_free(struct lyxml_ctx *xmlctx);
/**
+ * @brief Create a backup of XML context.
+ *
+ * @param[in] xmlctx XML context to back up.
+ * @param[out] backup Backup XML context.
+ * @return LY_ERR value.
+ */
+LY_ERR lyxml_ctx_backup(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup);
+
+/**
+ * @brief Restore previous backup of XML context.
+ *
+ * @param[in,out] xmlctx XML context to restore.
+ * @param[in] backup Backup XML context to restore, is unasble afterwards.
+ */
+void lyxml_ctx_restore(struct lyxml_ctx *xmlctx, struct lyxml_ctx *backup);
+
+/**
* @brief Compare values and their prefix mappings.
*
* @param[in] ctx1 Libyang context for resolving prefixes in @p value1.
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index b83432c..1e74bb9 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -251,6 +251,13 @@
CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<foo3 xmlns=\"urn:tests:a\"/>\n");
lyd_free_all(tree);
+ /* list, opaq flag */
+ data = "<l1 xmlns=\"urn:tests:a\"/>";
+ CHECK_PARSE_LYD(data, LYD_PARSE_OPAQ | LYD_PARSE_ONLY, 0, tree);
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 0, LY_VALUE_XML, "l1", 0, 0, NULL, 0, "");
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, "<l1 xmlns=\"urn:tests:a\"/>\n");
+ lyd_free_all(tree);
+
/* missing key, no flags */
data = "<l1 xmlns=\"urn:tests:a\">\n"
" <a>val_a</a>\n"