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;
}