XML data FEATURE parse/print XML attributes mapped to Metadata annotations
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 6685e8b..d967fe9 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -67,6 +67,16 @@
     return ly_ctx_get_module_implemented_ns(ctx, ns->uri);
 }
 
+struct attr_data_s {
+    const char *prefix;
+    const char *name;
+    char *value;
+    size_t prefix_len;
+    size_t name_len;
+    size_t value_len;
+    int dynamic;
+};
+
 /**
  * @brief Parse XML attributes of the XML element of YANG data.
  *
@@ -77,23 +87,13 @@
  * @reutn LY_ERR value.
  */
 static LY_ERR
-lydxml_attributes(struct lyd_xml_ctx *ctx, const char **data, struct lyd_attr **attributes)
+lydxml_attributes_parse(struct lyd_xml_ctx *ctx, const char **data, struct ly_set *attrs_data)
 {
     LY_ERR ret = LY_SUCCESS;
-    unsigned int u, v;
+    unsigned int u;
     const char *prefix, *name;
     size_t prefix_len, name_len;
-    struct lyd_attr *attr = NULL, *last = NULL;
-    const struct lyxml_ns *ns;
-    struct ly_set attr_datas = {0};
-    struct attr_data_s {
-        const char *prefix;
-        char *value;
-        size_t prefix_len;
-        size_t value_len;
-        int dynamic;
-    } *attr_data;
-    struct lys_module *mod;
+    struct attr_data_s *attr_data;
 
     while(ctx->status == LYXML_ATTRIBUTE &&
             lyxml_get_attribute((struct lyxml_context*)ctx, data, &prefix, &prefix_len, &name, &name_len) == LY_SUCCESS) {
@@ -110,60 +110,122 @@
          * annotation definition for the attribute and correctly process the value */
         attr_data = malloc(sizeof *attr_data);
         attr_data->prefix = prefix;
+        attr_data->name = name;
         attr_data->prefix_len = prefix_len;
+        attr_data->name_len = name_len;
         ret = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &attr_data->value, &attr_data->value_len, &attr_data->dynamic);
-        LY_CHECK_ERR_GOTO(ret, free(attr_data), cleanup);
-        ly_set_add(&attr_datas, attr_data, LY_SET_OPT_USEASLIST);
+        LY_CHECK_ERR_GOTO(ret, free(attr_data), error);
+        ly_set_add(attrs_data, attr_data, LY_SET_OPT_USEASLIST);
+    }
+
+    return LY_SUCCESS;
+
+error:
+    for (u = 0; u < attrs_data->count; ++u) {
+        if (((struct attr_data_s*)attrs_data->objs[u])->dynamic) {
+            free(((struct attr_data_s*)attrs_data->objs[u])->value);
+        }
+    }
+    ly_set_erase(attrs_data, free);
+    return ret;
+}
+
+static LY_ERR
+lydxml_attributes(struct lyd_xml_ctx *ctx, struct ly_set *attrs_data, struct lyd_node *parent)
+{
+    LY_ERR ret = LY_EVALID, rc;
+    struct lyd_attr *attr = NULL, *last = NULL;
+    const struct lyxml_ns *ns;
+    struct lys_module *mod;
+
+    for (unsigned int u = 0; u < attrs_data->count; ++u) {
+        unsigned int v;
+        struct lysc_ext_instance *ant = NULL;
+        struct attr_data_s *attr_data = (struct attr_data_s*)attrs_data->objs[u];
+
+        if (!attr_data->prefix_len) {
+            /* in XML, all attributes must be prefixed
+             * TODO exception for NETCONF filters which are supposed to map to the ietf-netconf without prefix */
+            if (ctx->options & LYD_OPT_STRICT) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Missing mandatory prefix for XML attribute \"%.*s\".",
+                       attr_data->name_len, attr_data->name);
+            }
+skip_attr:
+            if (attr_data->dynamic) {
+                free(attr_data->value);
+                attr_data->dynamic = 0;
+            }
+            continue;
+        }
+
+        /* get namespace of the attribute to find its annotation definition */
+        ns = lyxml_ns_get((struct lyxml_context *)ctx, attr_data->prefix, attr_data->prefix_len);
+        if (!ns) {
+            /* unknown namespace, ignore the attribute */
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", attr_data->prefix_len, attr_data->prefix);
+            goto cleanup;
+        }
+        mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
+        if (!mod) {
+            /* module is not implemented or not present in the schema */
+            if (ctx->options & LYD_OPT_STRICT) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE,
+                       "Unknown (or not implemented) YANG module with namespace \"%s\" for attribute \"%.*s%s%.*s\".",
+                       ns, attr_data->prefix_len, attr_data->prefix, attr_data->prefix_len ? ":" : "", attr_data->name_len, attr_data->name);
+            }
+            goto skip_attr;
+        }
+
+        LY_ARRAY_FOR(mod->compiled->exts, v) {
+            if (mod->compiled->exts[v].def->plugin == lyext_plugins_internal[LYEXT_PLUGIN_INTERNAL_ANNOTATION].plugin &&
+                    !strncmp(mod->compiled->exts[v].argument, attr_data->name, attr_data->name_len) && !mod->compiled->exts[v].argument[attr_data->name_len]) {
+                /* we have the annotation definition */
+                ant = &mod->compiled->exts[v];
+                break;
+            }
+        }
+        if (!ant) {
+            /* attribute is not defined as a metadata annotation (RFC 7952) */
+            if (ctx->options & LYD_OPT_STRICT) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Annotation definition for attribute \"%s:%.*s\" not found.",
+                       mod->name, attr_data->name_len, attr_data->name);
+            }
+            goto skip_attr;
+        }
 
         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);
+        attr->parent = parent;
+        attr->annotation = ant;
+        rc = lyd_value_parse_attr(attr, attr_data->value, attr_data->value_len, attr_data->dynamic, 0, lydxml_resolve_prefix, ctx, LYD_XML, NULL);
+        if (rc == LY_EINCOMPLETE) {
+            ly_set_add(&ctx->incomplete_type_validation_attrs, attr, LY_SET_OPT_USEASLIST);
+        } else if (rc) {
+            ret = rc;
+            free(attr);
+            goto cleanup;
+        }
+        attr_data->dynamic = 0; /* value eaten by lyd_value_parse_attr() */
+        attr->name = lydict_insert(ctx->ctx, attr_data->name, attr_data->name_len);
 
         if (last) {
             last->next = attr;
         } else {
-            (*attributes) = attr;
+            parent->attr = attr;
         }
         last = attr;
     }
-
-    /* resolve annotation pointers in all the attributes and process the attribute's values */
-    for (last = *attributes, u = 0; u < attr_datas.count && last; u++, last = last->next) {
-        attr_data = (struct attr_data_s*)attr_datas.objs[u];
-        ns = lyxml_ns_get((struct lyxml_context *)ctx, attr_data->prefix, attr_data->prefix_len);
-        mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
-
-        LY_ARRAY_FOR(mod->compiled->exts, v) {
-            if (mod->compiled->exts[v].def->plugin == lyext_plugins_internal[LYEXT_PLUGIN_INTERNAL_ANNOTATION].plugin &&
-                    !strcmp(mod->compiled->exts[v].argument, last->name)) {
-                /* we have the annotation definition */
-                last->annotation = &mod->compiled->exts[v];
-                break;
-            }
-        }
-
-        if (!last->annotation) {
-            /* attribute is not defined as a metadata annotation (RFC 7952) */
-
-        }
-
-        ret = lyd_value_parse_attr(attr, attr_data->value, attr_data->value_len, attr_data->dynamic, 0, lydxml_resolve_prefix, ctx, LYD_XML, NULL);
-        if (ret == LY_EINCOMPLETE) {
-            ly_set_add(&ctx->incomplete_type_validation_attrs, attr, LY_SET_OPT_USEASLIST);
-        } else if (ret) {
-            goto cleanup;
-        }
-        attr_data->dynamic = 0; /* value eaten by lyd_value_parse_attr() */
-    }
+    ret = LY_SUCCESS;
 
 cleanup:
 
-    for (u = 0; u < attr_datas.count; ++u) {
-        if (((struct attr_data_s*)attr_datas.objs[u])->dynamic) {
-            free(((struct attr_data_s*)attr_datas.objs[u])->value);
+    for (unsigned int u = 0; u < attrs_data->count; ++u) {
+        if (((struct attr_data_s*)attrs_data->objs[u])->dynamic) {
+            free(((struct attr_data_s*)attrs_data->objs[u])->value);
         }
     }
-    ly_set_erase(&attr_datas, free);
+    ly_set_erase(attrs_data, free);
+
     return ret;
 }
 
@@ -182,7 +244,7 @@
     LY_ERR ret = LY_SUCCESS;
     const char *prefix, *name;
     size_t prefix_len, name_len;
-    struct lyd_attr *attributes = NULL;
+    struct ly_set attrs_data = {0};
     const struct lyxml_ns *ns;
     const struct lysc_node *snode;
     struct lys_module *mod;
@@ -203,14 +265,13 @@
                 continue;
             }
         }
-        attributes = NULL;
         if (ctx->status == LYXML_ATTRIBUTE) {
-            LY_CHECK_GOTO(lydxml_attributes(ctx, data, &attributes), error);
+            LY_CHECK_GOTO(lydxml_attributes_parse(ctx, data, &attrs_data), error);
         }
 
         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);
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", prefix_len, prefix);
             goto error;
         }
         mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
@@ -338,8 +399,7 @@
             } /* first top level node - nothing more to do */
         }
         prev = last;
-        cur->attr = attributes;
-        attributes = NULL;
+        LY_CHECK_GOTO(ret = lydxml_attributes(ctx, &attrs_data, cur), cleanup);
 
         if (snode->nodetype & LYD_NODE_TERM) {
             int dynamic = 0;
@@ -442,7 +502,12 @@
     /* TODO add missing siblings default elements */
 
 cleanup:
-    lyd_free_attr(ctx->ctx, attributes, 1);
+    for (unsigned int u = 0; u < attrs_data.count; ++u) {
+        if (((struct attr_data_s*)attrs_data.objs[u])->dynamic) {
+            free(((struct attr_data_s*)attrs_data.objs[u])->value);
+        }
+    }
+    ly_set_erase(&attrs_data, free);
     return ret;
 
 error: