xml CHANGE hide namespace handling into the XML parser
Adding/removing namespaces can be done automaticcaly inside the parser
functions, so do not bother caller with it.
diff --git a/src/parser_xml.c b/src/parser_xml.c
index cdc8111..066a4d1 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -50,14 +50,13 @@
* @brief Parse XML attributes of the XML element of YANG data.
*
* @param[in] ctx XML YANG data parser context.
- * @param[in] element_name Element identifier to distinguish namespaces defined in different elements.
* @param[in,out] data Pointer to the XML string representation of the YANG data to parse.
* @param[out] attributes Resulting list of the parsed attributes. XML namespace definitions are not parsed
* as attributes, they are stored internally in the parser context.
* @reutn LY_ERR value.
*/
static LY_ERR
-lydxml_attributes(struct lyd_xml_ctx *ctx, const char *element_name, const char **data, struct lyd_attr **attributes)
+lydxml_attributes(struct lyd_xml_ctx *ctx, const char **data, struct lyd_attr **attributes)
{
LY_ERR ret = LY_SUCCESS;
unsigned int u;
@@ -78,38 +77,34 @@
char *buffer = NULL, *value;
size_t buffer_size = 0, value_len;
- lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
-
- if (prefix_len == 5 && !strncmp(prefix, "xmlns", 5)) {
- /* named namespace */
- lyxml_ns_add((struct lyxml_context *)ctx, element_name, name, name_len,
- dynamic ? value : strndup(value, value_len));
- } else if (!prefix && name_len == 5 && !strncmp(name, "xmlns", 5)) {
- /* default namespace */
- lyxml_ns_add((struct lyxml_context *)ctx, element_name, NULL, 0,
- dynamic ? value : strndup(value, value_len));
- } else {
- /* attribute */
- attr = calloc(1, sizeof *attr);
- LY_CHECK_ERR_GOTO(!attr, LOGMEM(ctx->ctx); ret = LY_EMEM, cleanup);
-
- attr->name = lydict_insert(ctx->ctx, name, name_len);
- /* auxiliary store the prefix information and wait with resolving prefix to the time when all the namespaces,
- * defined in this element, are parsed, so we will get the correct namespace for this prefix */
- attr_prefix = malloc(sizeof *attr_prefix);
- attr_prefix->prefix = prefix;
- attr_prefix->prefix_len = prefix_len;
- ly_set_add(&attr_prefixes, attr_prefix, LY_SET_OPT_USEASLIST);
-
- /* TODO process value */
-
- if (last) {
- last->next = attr;
- } else {
- (*attributes) = attr;
- }
- last = attr;
+ if (!name) {
+ /* seems like all the attrributes were internally processed as namespace definitions */
+ continue;
}
+
+ /* get attribute value */
+ ret = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &value, &value_len, &dynamic);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ attr = calloc(1, sizeof *attr);
+ LY_CHECK_ERR_GOTO(!attr, LOGMEM(ctx->ctx); ret = LY_EMEM, cleanup);
+
+ attr->name = lydict_insert(ctx->ctx, name, name_len);
+ /* auxiliary store the prefix information and wait with resolving prefix to the time when all the namespaces,
+ * defined in this element, are parsed, so we will get the correct namespace for this prefix */
+ attr_prefix = malloc(sizeof *attr_prefix);
+ attr_prefix->prefix = prefix;
+ attr_prefix->prefix_len = prefix_len;
+ ly_set_add(&attr_prefixes, attr_prefix, LY_SET_OPT_USEASLIST);
+
+ /* TODO process value */
+
+ if (last) {
+ last->next = attr;
+ } else {
+ (*attributes) = attr;
+ }
+ last = attr;
}
/* resolve annotation pointers in all the attributes */
@@ -141,7 +136,6 @@
{
LY_ERR ret = LY_SUCCESS;
const char *prefix, *name;
- char *element_name = NULL;
size_t prefix_len, name_len;
struct lyd_attr *attributes = NULL;
const struct lyxml_ns *ns;
@@ -157,9 +151,6 @@
LY_CHECK_GOTO(ret, cleanup);
if (!name) {
/* closing previous element */
- lyxml_ns_rm((struct lyxml_context *)ctx, element_name);
- free(element_name);
- element_name = NULL;
if (ctx->elements.count < parents_count) {
/* all siblings parsed */
break;
@@ -168,8 +159,7 @@
}
}
attributes = NULL;
- element_name = strndup(name, name_len);
- LY_CHECK_GOTO(lydxml_attributes(ctx, element_name, data, &attributes), cleanup);
+ LY_CHECK_GOTO(lydxml_attributes(ctx, data, &attributes), cleanup);
ns = lyxml_ns_get((struct lyxml_context *)ctx, prefix, prefix_len);
if (!ns) {
LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%*.s\".", prefix_len, prefix);
@@ -251,9 +241,7 @@
}
cleanup:
- lyxml_ns_rm((struct lyxml_context *)ctx, element_name);
lyd_free_attr(ctx->ctx, attributes, 1);
- free(element_name);
return ret;
}
diff --git a/src/xml.c b/src/xml.c
index 195ce56..89fc353 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -168,6 +168,102 @@
return LY_SUCCESS;
}
+/**
+ * @brief Add namespace definition into XML context.
+ *
+ * Namespaces from a single element are supposed to be added sequentially together (not interleaved by a namespace from other
+ * element). This mimic namespace visibility, since the namespace defined in element E is not visible from its parents or
+ * siblings. On the other hand, namespace from a parent element can be redefined in a child element. This is also reflected
+ * by lyxml_ns_get() which returns the most recent namespace definition for the given prefix.
+ *
+ * When leaving processing of a subtree of some element (after it is removed from context->elements), caller is supposed to call
+ * lyxml_ns_rm() to remove all the namespaces defined in such an element from the context.
+ *
+ * @param[in] context XML context to work with.
+ * @param[in] prefix Pointer to the namespace prefix as taken from lyxml_get_attribute(). Can be NULL for default namespace.
+ * @param[in] prefix_len Length of the prefix string (since it is not NULL-terminated when returned from lyxml_get_attribute()).
+ * @param[in] uri Namespace URI (value) to store. Value can be obtained via lyxml_get_string() and caller is not supposed to
+ * work with the pointer when the function succeeds. In case of error the value is freed.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lyxml_ns_add(struct lyxml_context *context, const char *prefix, size_t prefix_len, char *uri)
+{
+ struct lyxml_ns *ns;
+
+ ns = malloc(sizeof *ns);
+ LY_CHECK_ERR_RET(!ns, LOGMEM(context->ctx), LY_EMEM);
+
+ /* we need to connect the depth of the element where the namespace is defined with the
+ * namespace record to be able to maintain (remove) the record when the parser leaves
+ * (to its sibling or back to the parent) the element where the namespace was defined */
+ ns->depth = context->elements.count;
+
+ ns->uri = uri;
+ if (prefix) {
+ ns->prefix = strndup(prefix, prefix_len);
+ LY_CHECK_ERR_RET(!ns->prefix, LOGMEM(context->ctx); free(ns->uri); free(ns), LY_EMEM);
+ } else {
+ ns->prefix = NULL;
+ }
+
+ LY_CHECK_ERR_RET(ly_set_add(&context->ns, ns, LY_SET_OPT_USEASLIST) == -1,
+ free(ns->prefix); free(ns->uri); free(ns), LY_EMEM);
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Remove all the namespaces defined in the element recently closed (removed from the context->elements).
+ *
+ * @param[in] context XML context to work with.
+ * @return LY_ERR values.
+ */
+static LY_ERR
+lyxml_ns_rm(struct lyxml_context *context)
+{
+ unsigned int u;
+
+ for (u = context->ns.count - 1; u + 1 > 0; --u) {
+ if (((struct lyxml_ns *)context->ns.objs[u])->depth != context->elements.count + 1) {
+ /* we are done, the namespaces from a single element are supposed to be together */
+ break;
+ }
+ /* remove the ns structure */
+ free(((struct lyxml_ns *)context->ns.objs[u])->prefix);
+ free(((struct lyxml_ns *)context->ns.objs[u])->uri);
+ free(context->ns.objs[u]);
+ --context->ns.count;
+ }
+
+ if (!context->ns.count) {
+ /* cleanup the context's namespaces storage */
+ ly_set_erase(&context->ns, NULL);
+ }
+
+ return LY_SUCCESS;
+}
+
+const struct lyxml_ns *
+lyxml_ns_get(struct lyxml_context *context, const char *prefix, size_t prefix_len)
+{
+ unsigned int u;
+ struct lyxml_ns *ns;
+
+ for (u = context->ns.count - 1; u + 1 > 0; --u) {
+ ns = (struct lyxml_ns *)context->ns.objs[u];
+ if (prefix) {
+ if (!strncmp(prefix, ns->prefix, prefix_len) && ns->prefix[prefix_len] == '\0') {
+ return ns;
+ }
+ } else if (!ns->prefix) {
+ /* default namespace */
+ return ns;
+ }
+ }
+
+ return NULL;
+}
+
LY_ERR
lyxml_get_string(struct lyxml_context *context, const char **input, char **buffer, size_t *buffer_size, char **output, size_t *length, int *dynamic)
{
@@ -190,7 +286,7 @@
uint32_t n;
size_t u, newlines;
bool empty_content = false;
- LY_ERR rc;
+ LY_ERR rc = LY_SUCCESS;
assert(context);
assert(context->status == LYXML_ELEM_CONTENT || context->status == LYXML_ATTR_CONTENT);
@@ -402,11 +498,14 @@
/* remove the closed element record from the tags list */
free(context->elements.objs[context->elements.count - 1]);
--context->elements.count;
+
+ /* remove also the namespaces conneted with the element */
+ rc = lyxml_ns_rm(context);
}
}
(*input) = in;
- return LY_SUCCESS;
+ return rc;
#undef BUFSIZE
#undef BUFSIZE_STEP
@@ -424,7 +523,11 @@
LY_ERR rc;
unsigned int c;
size_t endtag_len;
+ int is_ns = 0;
+ const char *ns_prefix = NULL;
+ size_t ns_prefix_len = 0;
+start:
/* initialize output variables */
(*prefix) = (*name) = NULL;
(*prefix_len) = (*name_len) = 0;
@@ -476,6 +579,36 @@
}
context->status = LYXML_ATTR_CONTENT;
+ is_ns = 0;
+ if (*prefix && *prefix_len == 5 && !strncmp(*prefix, "xmlns", 5)) {
+ is_ns = 1;
+ ns_prefix = *name;
+ ns_prefix_len = *name_len;
+ } else if (*name_len == 5 && !strncmp(*name, "xmlns", 5)) {
+ is_ns = 1;
+ }
+ if (is_ns) {
+ /* instead of attribute, we have namespace specification,
+ * so process it automatically and then move to another attribute (if any) */
+ char *value = NULL;
+ size_t value_len = 0;
+ int dynamic = 0;
+
+ LY_CHECK_RET(lyxml_get_string(context, &in, &value, &value_len, &value, &value_len, &dynamic));
+ if ((rc = lyxml_ns_add(context, ns_prefix, ns_prefix_len, dynamic ? value : strndup(value, value_len)))) {
+ if (dynamic) {
+ free(value);
+ return rc;
+ }
+ }
+ if (context->status == LYXML_ATTRIBUTE) {
+ goto start;
+ } else {
+ (*prefix) = (*name) = NULL;
+ (*prefix_len) = (*name_len) = 0;
+ }
+ }
+
/* move caller's input */
(*input) = in;
return LY_SUCCESS;
@@ -602,6 +735,10 @@
/* opening and closing element tags matches, remove record from the opening tags list */
free(e);
--context->elements.count;
+
+ /* remove also the namespaces conneted with the element */
+ rc = lyxml_ns_rm(context);
+
/* do not return element information to announce closing element being currently processed */
*name = *prefix = NULL;
*name_len = *prefix_len = 0;
@@ -650,82 +787,6 @@
return LY_SUCCESS;
}
-LY_ERR
-lyxml_ns_add(struct lyxml_context *context, const char *element_name, const char *prefix, size_t prefix_len, char *uri)
-{
- struct lyxml_ns *ns;
-
- ns = malloc(sizeof *ns);
- LY_CHECK_ERR_RET(!ns, LOGMEM(context->ctx), LY_EMEM);
-
- /* to distinguish 2 elements, we need not only the name, but also its depth in the XML tree.
- * In case some dictionary is used to store elements' names (so name strings of 2 distinguish nodes
- * actually points to the same memory), so the depth is necessary to distinguish parent/child nodes
- * of the same name. Otherwise, the namespace defined in parent could be removed when leaving child node. */
- ns->element_depth = context->elements.count;
- ns->element = element_name;
-
- ns->uri = uri;
- if (prefix) {
- ns->prefix = strndup(prefix, prefix_len);
- LY_CHECK_ERR_RET(!ns->prefix, LOGMEM(context->ctx); free(ns), LY_EMEM);
- } else {
- ns->prefix = NULL;
- }
-
- LY_CHECK_ERR_RET(ly_set_add(&context->ns, ns, LY_SET_OPT_USEASLIST) == -1, free(ns->prefix), LY_EMEM);
- return LY_SUCCESS;
-}
-
-const struct lyxml_ns *
-lyxml_ns_get(struct lyxml_context *context, const char *prefix, size_t prefix_len)
-{
- unsigned int u;
- struct lyxml_ns *ns;
-
- for (u = context->ns.count - 1; u + 1 > 0; --u) {
- ns = (struct lyxml_ns *)context->ns.objs[u];
- if (prefix) {
- if (!strncmp(prefix, ns->prefix, prefix_len) && ns->prefix[prefix_len] == '\0') {
- return ns;
- }
- } else if (!ns->prefix) {
- /* default namespace */
- return ns;
- }
- }
-
- return NULL;
-}
-
-LY_ERR
-lyxml_ns_rm(struct lyxml_context *context, const char *element_name)
-{
- unsigned int u;
-
- for (u = context->ns.count - 1; u + 1 > 0; --u) {
- if (((struct lyxml_ns *)context->ns.objs[u])->element != element_name ||
- ((struct lyxml_ns *)context->ns.objs[u])->element_depth != context->elements.count + 1) {
- /* we are done, the namespaces from a single element are supposed to be together;
- * the second condition is there to distinguish parent/child elements with the same name
- * (which are for some reason stored at the same memory chunk), so we need to distinguish
- * level of the node */
- break;
- }
- /* remove the ns structure */
- free(((struct lyxml_ns *)context->ns.objs[u])->prefix);
- free(((struct lyxml_ns *)context->ns.objs[u])->uri);
- free(context->ns.objs[u]);
- --context->ns.count;
- }
-
- if (!context->ns.count) {
- /* cleanup the context's namespaces storage */
- ly_set_erase(&context->ns, NULL);
- }
-
- return LY_SUCCESS;
-}
void
lyxml_context_clear(struct lyxml_context *context)
diff --git a/src/xml.h b/src/xml.h
index 3224e8d..b5e40a3 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -48,10 +48,9 @@
(c >= 0x10000 && c <= 0xeffff))
struct lyxml_ns {
- const char *element; /* element where the namespace is defined */
char *prefix; /* prefix of the namespace, NULL for the default namespace */
char *uri; /* namespace URI */
- unsigned int element_depth; /* depth level of the element to distinguish parent-child elements of the same name */
+ unsigned int depth; /* depth level of the element to maintain the list of accessible namespace definitions */
};
/* element tag identifier for matching opening and closing tags */
@@ -111,15 +110,19 @@
* Input string is not being modified, so the returned values are not NULL-terminated, instead their length
* is returned.
*
- * In case of a namespace definition, prefix just contains xmlns string. In case of the default namespace,
- * prefix is NULL and the attribute name is xmlns.
+ * Namespace definitions are processed automatically and stored internally. To get namespace for a specific
+ * prefix, use lyxml_get_ns(). This also means, that in case there are only the namespace definitions,
+ * lyxml_get_attribute() can succeed, but nothing (name, prefix) is returned.
+ *
+ * The status member of the context is updated to provide information what the caller is supposed to call
+ * after this function.
*
* @param[in] context XML context to track lines or store errors into libyang context.
* @param[in,out] input Input string to process, updated according to the processed/read data so,
* when succeeded, it points to the opening quote of the attribute's value.
* @param[out] prefix Pointer to prefix if present in the attribute name, NULL otherwise.
* @param[out] prefix_len Length of the prefix if any.
- * @param[out] name Attribute name.
+ * @param[out] name Attribute name. Can be NULL only in case there is actually no attribute, but namespaces.
* @param[out] name_len Length of the element name.
* @return LY_ERR values.
*/
@@ -159,28 +162,6 @@
LY_ERR lyxml_get_string(struct lyxml_context *context, const char **input, char **buffer, size_t *buffer_size, char **output, size_t *length, int *dynamic);
/**
- * @brief Add namespace definition into XML context.
- *
- * Namespaces from a single element are supposed to be added sequentially together (not interleaved by a namespace from other
- * element). This mimic namespace visibility, since the namespace defined in element E is not visible from its parents or
- * siblings. On the other hand, namespace from a parent element can be redefined in a child element. This is also reflected
- * by lyxml_ns_get() which returns the most recent namespace definition for the given prefix.
- *
- * When leaving processing of a subtree of some element, caller is supposed to call lyxml_ns_rm() to remove all the namespaces
- * defined in such an element from the context.
- *
- * @param[in] context XML context to work with.
- * @param[in] element_name Pointer to the element name where the namespace is defined. Serve as an identifier to select
- * which namespaces are supposed to be removed via lyxml_ns_rm() when leaving the element's subtree.
- * @param[in] prefix Pointer to the namespace prefix as taken from lyxml_get_attribute(). Can be NULL for default namespace.
- * @param[in] prefix_len Length of the prefix string (since it is not NULL-terminated when returned from lyxml_get_attribute()).
- * @param[in] uri Namespace URI (value) to store. Value can be obtained via lyxml_get_string() and caller is not supposed to
- * work with the pointer when the function succeeds.
- * @return LY_ERR values.
- */
-LY_ERR lyxml_ns_add(struct lyxml_context *context, const char *element_name, const char *prefix, size_t prefix_len, char *uri);
-
-/**
* @brief Get a namespace record for the given prefix in the current context.
*
* @param[in] context XML context to work with.
@@ -193,16 +174,6 @@
const struct lyxml_ns *lyxml_ns_get(struct lyxml_context *context, const char *prefix, size_t prefix_len);
/**
- * @brief Remove all the namespaces defined in the given element.
- *
- * @param[in] context XML context to work with.
- * @param[in] element_name Pointer to the element name where the namespaces are defined. Serve as an identifier previously provided
- * by lyxml_get_element()
- * @return LY_ERR values.
- */
-LY_ERR lyxml_ns_rm(struct lyxml_context *context, const char *element_name);
-
-/**
* @brief Print the given @p text as XML string which replaces some of the characters which cannot appear in XML data.
*
* @param[in] out Output structure for printing.