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.
diff --git a/tests/src/test_xml.c b/tests/src/test_xml.c
index e153b92..b39b4b0 100644
--- a/tests/src/test_xml.c
+++ b/tests/src/test_xml.c
@@ -403,24 +403,21 @@
 {
     (void) state; /* unused */
 
-    const char *e1, *e2;
     const struct lyxml_ns *ns;
 
     struct lyxml_context ctx;
     memset(&ctx, 0, sizeof ctx);
     ctx.line = 1;
 
-    e1 = "element1";
-    e2 = "element2";
     /* simulate adding open element1 into context */
     ctx.elements.count++;
     /* processing namespace definitions */
-    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e1, NULL, 0, strdup("urn:default")));
-    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e1, "nc", 2, strdup("urn:nc1")));
+    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, NULL, 0, strdup("urn:default")));
+    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, "nc", 2, strdup("urn:nc1")));
     /* simulate adding open element2 into context */
     ctx.elements.count++;
     /* processing namespace definitions */
-    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e2, "nc", 2, strdup("urn:nc2")));
+    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, "nc", 2, strdup("urn:nc2")));
     assert_int_equal(3, ctx.ns.count);
     assert_int_not_equal(0, ctx.ns.size);
 
@@ -436,7 +433,7 @@
 
     /* simulate closing element2 */
     ctx.elements.count--;
-    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx, e2));
+    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx));
     assert_int_equal(2, ctx.ns.count);
 
     ns = lyxml_ns_get(&ctx, "nc", 2);
@@ -446,7 +443,7 @@
 
     /* simulate closing element1 */
     ctx.elements.count--;
-    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx, e1));
+    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx));
     assert_int_equal(0, ctx.ns.count);
 
     assert_null(lyxml_ns_get(&ctx, "nc", 2));
@@ -458,30 +455,25 @@
 {
     (void) state; /* unused */
 
-    const char *e1, *e2;
-
     struct lyxml_context ctx;
     memset(&ctx, 0, sizeof ctx);
     ctx.line = 1;
 
-    /* parent element has the same name as its child (and for some reason both names are stored at the same place) */
-    e1 = e2 = "element1";
     /* simulate adding open element1 into context */
     ctx.elements.count++;
     /* default namespace defined in parent element1 */
-    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, e1, NULL, 0, strdup("urn:default")));
+    assert_int_equal(LY_SUCCESS, lyxml_ns_add(&ctx, NULL, 0, strdup("urn:default")));
     assert_int_equal(1, ctx.ns.count);
     /* going into child element1 */
-    e1 = e2;
     /* simulate adding open element1 into context */
     ctx.elements.count++;
     /* no namespace defined, going out (first, simulate closing of so far open element) */
     ctx.elements.count--;
-    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx, e2));
+    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx));
     assert_int_equal(1, ctx.ns.count);
     /* nothing else, going out of the parent element1 (first, simulate closing of so far open element) */
     ctx.elements.count--;
-    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx, e1));
+    assert_int_equal(LY_SUCCESS, lyxml_ns_rm(&ctx));
     assert_int_equal(0, ctx.ns.count);
 }