data parser CHANGE make data parser accept empty data

In case a schema doesn't require some mandatory element, having an
empty data tree (represented as NULL) can be completely correct.
diff --git a/src/libyang.h b/src/libyang.h
index 70e5e00..bf4e05f 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -457,7 +457,9 @@
  * @param[in] ... Additional argument must be supplied when #LYD_OPT_RPCREPLY value is specified in \p options. The
  *            argument is supposed to provide pointer to the RPC schema node for the reply's request
  *            (const struct ::lys_node* rpc).
- * @return Pointer to the built data tree. To free the returned structure, use lyd_free().
+ * @return Pointer to the built data tree or NULL in case of empty \p data. To free the returned structure,
+ *         use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
+ *         #ly_errno contains appropriate error code (see #LY_ERR).
  */
 struct lyd_node *lyd_parse_data(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options, ...);
 
@@ -486,7 +488,9 @@
  * @param[in] ... Additional argument must be supplied when #LYD_OPT_RPCREPLY value is specified in \p options. The
  *            argument is supposed to provide pointer to the RPC schema node for the reply's request
  *            (const struct ::lys_node* rpc).
- * @return Pointer to the built data tree. To free the returned structure, use lyd_free().
+ * @return Pointer to the built data tree or NULL in case of empty \p root. To free the returned structure,
+ *         use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
+ *         #ly_errno contains appropriate error code (see #LY_ERR).
  */
 struct lyd_node *lyd_parse_xml(struct ly_ctx *ctx, struct lyxml_elem **root, int options,...);
 
@@ -507,7 +511,9 @@
  * @param[in] ... Additional argument must be supplied when #LYD_OPT_RPCREPLY value is specified in \p options. The
  *            argument is supposed to provide pointer to the RPC schema node for the reply's request
  *            (const struct ::lys_node* rpc).
- * @return Pointer to the built data tree. To free the returned structure, use lyd_free().
+ * @return Pointer to the built data tree or NULL in case of empty file. To free the returned structure,
+ *         use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
+ *         #ly_errno contains appropriate error code (see #LY_ERR).
  */
 struct lyd_node *lyd_parse_fd(struct ly_ctx *ctx, int fd, LYD_FORMAT format, int options, ...);
 
@@ -526,7 +532,9 @@
  * @param[in] ... Additional argument must be supplied when #LYD_OPT_RPCREPLY value is specified in \p options. The
  *            argument is supposed to provide pointer to the RPC schema node for the reply's request
  *            (const struct ::lys_node* rpc).
- * @return Pointer to the built data tree. To free the returned structure, use lyd_free().
+ * @return Pointer to the built data tree or NULL in case of empty file. To free the returned structure,
+ *         use lyd_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case of error,
+ *         #ly_errno contains appropriate error code (see #LY_ERR).
  */
 struct lyd_node *lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options, ...);
 
diff --git a/src/parser_json.c b/src/parser_json.c
index 57e87a5..6c5d077 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1078,6 +1078,8 @@
     unsigned int len = 0, r;
     struct attr_cont *attrs = NULL;
 
+    ly_errno = LY_SUCCESS;
+
     if (!ctx || !data) {
         LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
         return NULL;
@@ -1092,7 +1094,6 @@
 #ifndef NDEBUG
     lineno = 0;
 #endif
-    ly_errno = 0;
 
     /* skip leading whitespaces */
     len += skip_ws(&data[len]);
diff --git a/src/parser_xml.c b/src/parser_xml.c
index ccae5aa..c4cd7a5 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -144,8 +144,12 @@
     *result = NULL;
 
     if (!xml->ns || !xml->ns->value) {
-        LOGVAL(LYE_XML_MISS, LOGLINE(xml), "element's", "namespace");
-        return -1;
+        if (options & LYD_OPT_STRICT) {
+            LOGVAL(LYE_XML_MISS, LOGLINE(xml), "element's", "namespace");
+            return -1;
+        } else {
+            return 0;
+        }
     }
 
     /* find schema node */
@@ -419,11 +423,19 @@
     struct lyd_node *result = NULL, *next, *iter, *last;
     struct lyxml_elem *xmlstart, *xmlelem, *xmlaux;
 
+    ly_errno = LY_SUCCESS;
+
     if (!ctx || !root) {
         LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
         return NULL;
     }
 
+    if (!(*root)) {
+        /* empty tree - no work is needed */
+        lyd_validate(NULL, options);
+        return NULL;
+    }
+
     if (lyp_check_options(options)) {
         LOGERR(LY_EINVAL, "%s: Invalid options (multiple data type flags set).", __func__);
         return NULL;
@@ -484,11 +496,6 @@
         }
     }
 
-    if (!result) {
-        LOGERR(LY_EVALID, "Model for the data to be linked with not found.");
-        goto cleanup;
-    }
-
     /* check leafrefs and/or instids if any */
     if (result && resolve_unres_data(unres)) {
         /* leafref & instid checking failed */
diff --git a/src/printer.c b/src/printer.c
index 9e43040..0243a20 100644
--- a/src/printer.c
+++ b/src/printer.c
@@ -230,6 +230,14 @@
 static int
 lyd_print_(struct lyout *out, const struct lyd_node *root, LYD_FORMAT format, int options)
 {
+    if (!root) {
+        /* no data to print, but even empty tree is valid */
+        if (out->type == LYOUT_MEMORY || out->type == LYOUT_CALLBACK) {
+            ly_print(out, "");
+        }
+        return EXIT_SUCCESS;
+    }
+
     switch (format) {
     case LYD_XML:
         return xml_print_data(out, root, 0, options);
@@ -248,7 +256,7 @@
 {
     struct lyout out;
 
-    if (!f || !root) {
+    if (!f) {
         ly_errno = LY_EINVAL;
         return EXIT_FAILURE;
     }
@@ -264,7 +272,7 @@
 {
     struct lyout out;
 
-    if (fd < 0 || !root) {
+    if (fd < 0) {
         ly_errno = LY_EINVAL;
         return EXIT_FAILURE;
     }
@@ -281,7 +289,7 @@
     struct lyout out;
     int r;
 
-    if (!strp || !root) {
+    if (!strp) {
         ly_errno = LY_EINVAL;
         return EXIT_FAILURE;
     }
@@ -303,7 +311,7 @@
 {
     struct lyout out;
 
-    if (!writeclb || !root) {
+    if (!writeclb) {
         ly_errno = LY_EINVAL;
         return EXIT_FAILURE;
     }
diff --git a/src/tree_data.c b/src/tree_data.c
index a4fa9cd..56ed092 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -76,6 +76,10 @@
         return NULL;
     }
 
+    if (!result && !ly_errno) {
+        /* is empty data tree really valid ? */
+        lyd_validate(NULL, options);
+    }
     return result;
 }
 
@@ -664,12 +668,55 @@
 }
 
 API int
-lyd_validate(struct lyd_node *node, int options)
+lyd_validate(struct lyd_node *node, int options, ...)
 {
     struct lyd_node *root, *next1, *next2, *iter, *to_free = NULL;
+    const struct lys_node *schema;
+    struct ly_ctx *ctx;
+    int i;
+    va_list ap;
 
     ly_errno = 0;
 
+    if (!node) {
+        /* TODO what about LYD_OPT_NOTIF, LYD_OPT_RPC and LYD_OPT_RPCREPLY ? */
+        if (options & (LYD_OPT_FILTER | LYD_OPT_EDIT | LYD_OPT_GET | LYD_OPT_GETCONFIG)) {
+            return EXIT_SUCCESS;
+        }
+        /* LYD_OPT_DATA || LYD_OPT_CONFIG */
+
+        /* get context with schemas from the variable arguments */
+        va_start(ap, options);
+        ctx = va_arg(ap,  struct ly_ctx*);
+        if (!ctx) {
+            LOGERR(LY_EINVAL, "%s: Invalid variable argument.", __func__);
+            va_end(ap);
+            return EXIT_FAILURE;
+        }
+
+        /* check for missing mandatory elements according to schemas in context */
+        for (i = 0; i < ctx->models.used; i++) {
+            if (!ctx->models.list[i]->data) {
+                continue;
+            }
+            schema = ly_check_mandatory(NULL, ctx->models.list[i]->data);
+            if (schema) {
+                if (schema->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+                    LOGVAL(LYE_SPEC, 0, "Number of \"%s\" instances in \"%s\" does not follow min-elements constraint.",
+                           schema->name, schema->parent ? schema->parent->name : ctx->models.list[i]->name);
+                } else {
+                    LOGVAL(LYE_MISSELEM, 0, schema->name, schema->parent ? schema->parent->name : ctx->models.list[i]->name);
+                }
+                va_end(ap);
+                return EXIT_FAILURE;
+
+            }
+        }
+
+        va_end(ap);
+        return EXIT_SUCCESS;
+    }
+
     if (!(options & LYD_OPT_NOSIBLINGS)) {
         /* check that the node is the first sibling */
         while(node->prev->next) {
diff --git a/src/tree_data.h b/src/tree_data.h
index a2cd106..55d2fd2 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -322,7 +322,7 @@
  *
  * @param[in] data A node in the data tree to search.
  * @param[in] schema Schema node of the data nodes caller want to find.
- * @return Set of found data nodes (use ::ly_set#dset). If no data node is found, the returned set is empty.
+ * @return Set of found data nodes (use dset member of ::ly_set). If no data node is found, the returned set is empty.
  * In case of error, NULL is returned.
  */
 struct ly_set *lyd_get_node(const struct lyd_node *data, const struct lys_node *schema);
@@ -332,11 +332,12 @@
  *
  * @param[in] node Data tree to be validated.
  * @param[in] options Options for the inserting data to the target data tree options, see @ref parseroptions.
+ * @param[in] ctx libyang context for the data (used only in case the \p node is NULL, so in case of checking empty data tree)
  * @return 0 on success (if options include #LYD_OPT_FILTER, some nodes could still have been deleted as an
  * optimization, which can have a bad consequences when the \p node stores a subtree instead of a tree with
  * a top-level node(s)), nonzero in case of an error.
  */
-int lyd_validate(struct lyd_node *node, int options);
+int lyd_validate(struct lyd_node *node, int options, ...);
 
 /**
  * @brief Unlink the specified data subtree. All referenced namespaces are copied.
diff --git a/src/tree_internal.h b/src/tree_internal.h
index c544b63..91f2af9 100644
--- a/src/tree_internal.h
+++ b/src/tree_internal.h
@@ -237,12 +237,14 @@
  * Besides the mandatory statements, also min-elements and max-elements constraints in
  * lists and leaf-list are checked.
  *
- * @param[in] start Root node for the searching subtree. Expecting that all child instances
+ * @param[in] data Root node for the searching subtree. Expecting that all child instances
  * are already resolved. Note that the \p start node itself is not checked since it must be present.
+ * @param[in] schema To check mandatory elements in empty data tree (\p data is NULL), we need
+ * the first schema node in a schema to be checked.
  * @return The first mandatory element definition not present in the data, NULL if
  * there is no such element in the \p starts's subtree.
  */
-const struct lys_node *ly_check_mandatory(const struct lyd_node *start);
+const struct lys_node *ly_check_mandatory(const struct lyd_node *data, const struct lys_node *schema);
 
 /**
  * @brief Find the parent node of an attribute.
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 3eb39ca..7b6144d 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -316,11 +316,11 @@
 }
 
 static const struct lys_node *
-check_mand_getnext(const struct lys_node *last, const struct lys_node *parent)
+check_mand_getnext(const struct lys_node *last, const struct lys_node *parent, const struct lys_module *module)
 {
     const struct lys_node *next;
 
-    next = lys_getnext(last, parent, NULL, LYS_GETNEXT_WITHCHOICE);
+    next = lys_getnext(last, parent, module, LYS_GETNEXT_WITHCHOICE);
 
 repeat:
     if (next && next->nodetype == LYS_CONTAINER) {
@@ -348,93 +348,99 @@
     uint32_t minmax;
 
     if (node->flags & LYS_MAND_TRUE) {
-       switch (node->nodetype) {
-       case LYS_LEAF:
-       case LYS_ANYXML:
-       case LYS_CHOICE:
-           if (node->parent->nodetype == LYS_CASE) {
-               /* 7.6.5, rule 2 */
-               /* 7.9.4, rule 1 */
-               if (node->parent->parent->parent == data->schema) {
-                   /* the only case the node's siblings can exist is that the
-                    * data node passed originaly to ly_check_mandatory()
-                    * had this choice as a child
-                    */
-                   /* try to find the node's siblings in data */
-                   LY_TREE_FOR(data->child, diter) {
-                       LY_TREE_FOR(node->parent->child, siter) {
-                           if (siter == diter->schema) {
-                               /* some sibling exists, rule applies */
-                               break;
-                           }
-                       }
-                       if (siter) {
-                           break;
-                       }
-                   }
-               }
-               if (!siter) {
-                   /* no sibling exists */
-                   return NULL;
-               }
-           } else {
-               for(parent = node->parent; parent != stop; parent = parent->parent) {
-                   if (parent->nodetype != LYS_CONTAINER) {
-                       /* 7.6.5, rule 1, checking presence is not needed
-                        * since it is done in check_mand_getnext()
-                        */
-                       ly_set_free(set);
-                       return NULL;
-                   }
-                   /* add the parent to the list for searching in data tree */
-                   if (!set) {
-                       set = ly_set_new();
-                   }
-                   /* ignore return - memory error is logged and we will
-                    * check at least the rest of nodes we have */
-                   (void) ly_set_add(set, parent);
-               }
-           }
+        if (!data) {
+            /* we have no data but a mandatory node */
+            return node;
+        }
+        switch (node->nodetype) {
+        case LYS_LEAF:
+        case LYS_ANYXML:
+        case LYS_CHOICE:
+            if (node->parent && node->parent->nodetype == LYS_CASE) {
+                /* 7.6.5, rule 2 */
+                /* 7.9.4, rule 1 */
+                if (node->parent->parent->parent == data->schema) {
+                    /* the only case the node's siblings can exist is that the
+                     * data node passed originally to ly_check_mandatory()
+                     * had this choice as a child
+                     */
+                    /* try to find the node's siblings in data */
+                    LY_TREE_FOR(data->child, diter) {
+                        LY_TREE_FOR(node->parent->child, siter) {
+                            if (siter == diter->schema) {
+                                /* some sibling exists, rule applies */
+                                break;
+                            }
+                        }
+                        if (siter) {
+                            break;
+                        }
+                    }
+                }
+                if (!siter) {
+                    /* no sibling exists */
+                    return NULL;
+                }
+            } else {
+                for (parent = node->parent; parent != stop; parent = parent->parent) {
+                    if (parent->nodetype != LYS_CONTAINER) {
+                        /* 7.6.5, rule 1, checking presence is not needed
+                         * since it is done in check_mand_getnext()
+                         */
+                        ly_set_free(set);
+                        return NULL;
+                    }
+                    /* add the parent to the list for searching in data tree */
+                    if (!set) {
+                        set = ly_set_new();
+                    }
+                    /* ignore return - memory error is logged and we will
+                     * check at least the rest of nodes we have */
+                    (void)ly_set_add(set, parent);
+                }
+            }
 
-           /* search for instance */
-           if (set) {
-               for (i = 0; i < set->number; i++) {
-                   LY_TREE_FOR(data->child, diter) {
-                       if (diter->schema == set->sset[i]) {
-                           break;
-                       }
-                   }
-                   if (!diter) {
-                       /* instance not found */
-                       node = set->sset[i];
-                       ly_set_free(set);
-                       return node;
-                   }
-                   data = diter;
-               }
-               ly_set_free(set);
-           }
+            /* search for instance */
+            if (set) {
+                for (i = 0; i < set->number; i++) {
+                    LY_TREE_FOR(data->child, diter) {
+                        if (diter->schema == set->sset[i]) {
+                            break;
+                        }
+                    }
+                    if (!diter) {
+                        /* instance not found */
+                        node = set->sset[i];
+                        ly_set_free(set);
+                        return node;
+                    }
+                    data = diter;
+                }
+                ly_set_free(set);
+            }
 
-           LY_TREE_FOR(data->child, diter) {
-               if (diter->schema == node) {
-                   return NULL;
-               }
-           }
+            LY_TREE_FOR(data->child, diter) {
+                if (diter->schema == node) {
+                    return NULL;
+                }
+            }
 
-           /* instance not found */
-           /* 7.6.5, rule 3 (or 2) */
-           /* 7.9.4, rule 2 */
-           return node;
-       default:
-           /* error */
-           break;
-       }
+            /* instance not found */
+            /* 7.6.5, rule 3 (or 2) */
+            /* 7.9.4, rule 2 */
+            return node;
+        default:
+            /* error */
+            break;
+        }
     } else if (node->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
         /* search for number of instances */
         minmax = 0;
-        LY_TREE_FOR(data->child, diter) {
-            if (diter->schema == node) {
-                minmax++;
+        if (data) {
+            LY_TREE_FOR(data->child, diter) {
+                if (diter->schema == node) {
+                    minmax++;
+                }
             }
         }
 
@@ -462,13 +468,19 @@
 }
 
 const struct lys_node *
-ly_check_mandatory(const struct lyd_node *data)
+ly_check_mandatory(const struct lyd_node *data, const struct lys_node *schema)
 {
     const struct lys_node *siter, *saux, *saux2, *result, *parent = NULL, *parent2;
     const struct lyd_node *diter;
     int found;
 
-    siter = data->schema->child;
+    assert(data || schema);
+
+    if (!data) { /* !data && schema */
+        siter = schema;
+    } else { /* data && !schema */
+        schema = siter = data->schema->child;
+    }
 
 repeat:
     while (siter) {
@@ -491,7 +503,7 @@
             /* ... and then the subtree */
             if (parent && siter->nodetype == LYS_CONTAINER && !((struct lys_node_container *)siter)->presence) {
                 saux = NULL;
-                while ((saux = check_mand_getnext(saux, siter))) {
+                while ((saux = check_mand_getnext(saux, siter, NULL))) {
                     result = check_mand_check(saux, siter, data);
                     if (result) {
                         return result;
@@ -507,7 +519,7 @@
             found = 0;
             parent2 = NULL;
 repeat_choice:
-            while (siter) {
+            while (siter && data) {
                 if (lys_is_disabled(siter, 2)) {
                     siter = siter->next;
                     continue;
@@ -529,7 +541,7 @@
                         /* check presence of mandatory siblings */
                         if (parent2 && parent2->nodetype == LYS_CASE) {
                             saux2 = NULL;
-                            while ((saux2 = check_mand_getnext(saux2, parent2))) {
+                            while ((saux2 = check_mand_getnext(saux2, parent2, NULL))) {
                                 result = check_mand_check(saux2, parent2, data);
                                 if (result) {
                                     return result;
@@ -595,7 +607,7 @@
 
     if (parent) {
         siter = parent->next;
-        if (parent->parent == data->schema) {
+        if (parent->parent == schema) {
             parent = NULL;
         } else {
             parent = parent->parent;
diff --git a/src/validation.c b/src/validation.c
index ae26f4f..2854808 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -423,7 +423,7 @@
         /* mandatory children */
         if ((schema->nodetype & (LYS_CONTAINER | LYS_LIST))
                 && !(options & (LYD_OPT_FILTER | LYD_OPT_EDIT | LYD_OPT_GET | LYD_OPT_GETCONFIG))) {
-            siter = ly_check_mandatory(node);
+            siter = ly_check_mandatory(node, NULL);
             if (siter) {
                 if (siter->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
                     LOGVAL(LYE_SPEC, line, "Number of \"%s\" instances in \"%s\" does not follow min/max constraints.",
diff --git a/src/xml.c b/src/xml.c
index 6f0c222..f466fca 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -1128,6 +1128,8 @@
     unsigned int len;
     struct lyxml_elem *root, *first = NULL, *next;
 
+    ly_errno = LY_SUCCESS;
+
 #ifndef NDEBUG
     /* TODO: threads support */
     lineno = 1;
diff --git a/src/xml.h b/src/xml.h
index 502c79c..8dfab0d 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -137,7 +137,9 @@
  * @param[in] data Pointer to a NULL-terminated string containing XML data to
  * parse.
  * @param[in] options Parser options, see @ref xmlreadoptions.
- * @return pointer to root of the parsed XML document tree.
+ * @return Pointer to the root of the parsed XML document tree or NULL in case of empty \p data. To free the
+ *         returned data, use lyxml_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case
+ *         of error, #ly_errno contains appropriate error code (see #LY_ERR).
  */
 struct lyxml_elem *lyxml_read_data(struct ly_ctx *ctx, const char *data, int options);
 
@@ -147,7 +149,9 @@
  * @param[in] ctx libyang context to use
  * @param[in] filename Path to the file where read data to parse
  * @param[in] options Parser options, see @ref xmlreadoptions.
- * @return pointer to root of the parsed XML document tree.
+ * @return Pointer to the root of the parsed XML document tree or NULL in case of empty file. To free the
+ *         returned data, use lyxml_free(). In these cases, the function sets #ly_errno to LY_SUCCESS. In case
+ *         of error, #ly_errno contains appropriate error code (see #LY_ERR).
  */
 struct lyxml_elem *lyxml_read_path(struct ly_ctx *ctx, const char *filename, int options);
 
diff --git a/tools/lint/commands.c b/tools/lint/commands.c
index 3e35b04..0063c20 100644
--- a/tools/lint/commands.c
+++ b/tools/lint/commands.c
@@ -427,7 +427,7 @@
 
         xml = lyxml_read_path(ctx, argv[optind], 0);
         if (!xml) {
-            fprintf(stderr, "Failed to parse XML data.\n");
+            fprintf(stderr, "Failed to parse XML data for automatic type detection.\n");
             goto cleanup;
         }
 
@@ -474,8 +474,7 @@
     } else {
         data = lyd_parse_path(ctx, argv[optind], informat, options);
     }
-
-    if (data == NULL) {
+    if (ly_errno) {
         fprintf(stderr, "Failed to parse data.\n");
         goto cleanup;
     }
@@ -596,8 +595,7 @@
 
     /* data file */
     data = lyd_parse_path(ctx, argv[optind], LYD_XML, 0);
-
-    if (data == NULL) {
+    if (ly_errno) {
         fprintf(stderr, "Failed to parse data.\n");
         goto cleanup;
     }