data tree FEATURE support for NETCONF messages
diff --git a/doc/transition.dox b/doc/transition.dox
index 966e26e..310ca46 100644
--- a/doc/transition.dox
+++ b/doc/transition.dox
@@ -275,9 +275,7 @@
* lyd_parse_fd() | ::lyd_parse_data_fd() | Renamed and limited to data trees only.
* lyd_parse_mem() | ::lyd_parse_data_mem() | ^
* lyd_parse_path() | ::lyd_parse_data_path() | ^
- * - | ::lyd_parse_notif() | Separated function to parse Notifications.
- * - | ::lyd_parse_rpc() | Separated function to parse RPCs.
- * - | ::lyd_parse_reply() | Separated function to parse replies to RPCs
+ * - | ::lyd_parse_op() | Separated function for parsing RPCs, actions, replies, and notifications.
* - | ::lyd_print_all() | New API accepting ::ly_out.
* - | ::lyd_print_tree() | ^
*
diff --git a/src/common.h b/src/common.h
index a9e100e..f03535d 100644
--- a/src/common.h
+++ b/src/common.h
@@ -46,6 +46,7 @@
#define GETMACRO3(_1, _2, _3, NAME, ...) NAME
#define GETMACRO4(_1, _2, _3, _4, NAME, ...) NAME
#define GETMACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME
+#define GETMACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
/*
* If the compiler supports attribute to mark objects as hidden, mark all
@@ -196,8 +197,10 @@
#define LY_CHECK_ARG_RET3(CTX, ARG1, ARG2, ARG3, RETVAL) LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG3, RETVAL)
#define LY_CHECK_ARG_RET4(CTX, ARG1, ARG2, ARG3, ARG4, RETVAL) LY_CHECK_ARG_RET3(CTX, ARG1, ARG2, ARG3, RETVAL);\
LY_CHECK_ARG_RET1(CTX, ARG4, RETVAL)
-#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO5(__VA_ARGS__, LY_CHECK_ARG_RET4, LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1)\
- (CTX, __VA_ARGS__)
+#define LY_CHECK_ARG_RET5(CTX, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL) LY_CHECK_ARG_RET4(CTX, ARG1, ARG2, ARG3, ARG4, RETVAL);\
+ LY_CHECK_ARG_RET1(CTX, ARG5, RETVAL)
+#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO6(__VA_ARGS__, LY_CHECK_ARG_RET5, LY_CHECK_ARG_RET4, LY_CHECK_ARG_RET3, \
+ LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1) (CTX, __VA_ARGS__)
/* count sequence size for LY_VCODE_INCHILDSTMT validation error code */
size_t LY_VCODE_INSTREXP_len(const char *str);
diff --git a/src/diff.c b/src/diff.c
index 67039af..ad72ed9 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -1001,7 +1001,7 @@
/* apply diff recursively */
LY_LIST_FOR(lyd_child_no_keys(diff_node), diff_child) {
- LY_CHECK_RET(lyd_diff_apply_r(lyd_node_children_p(match), match, diff_child, diff_cb, cb_data));
+ LY_CHECK_RET(lyd_diff_apply_r(lyd_node_child_p(match), match, diff_child, diff_cb, cb_data));
}
return LY_SUCCESS;
diff --git a/src/in.c b/src/in.c
index 8912b32..f4c8fd6 100644
--- a/src/in.c
+++ b/src/in.c
@@ -381,50 +381,108 @@
}
LY_ERR
+lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op)
+{
+ const struct lyd_node *iter;
+
+ *op = NULL;
+
+ if (!parent) {
+ /* no parent, nothing to look for */
+ return LY_SUCCESS;
+ }
+
+ /* we need to find the operation node if it already exists */
+ for (iter = parent; iter; iter = lyd_parent(iter)) {
+ if (iter->schema && (iter->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))) {
+ break;
+ }
+ }
+
+ if (!iter) {
+ /* no operation found */
+ return LY_SUCCESS;
+ }
+
+ if (!(int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY))) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent %s \"%s\" node when not parsing any operation.",
+ lys_nodetype2str(iter->schema->nodetype), iter->schema->name);
+ return LY_EINVAL;
+ } else if ((iter->schema->nodetype == LYS_RPC) && !(int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY))) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent RPC \"%s\" node when not parsing RPC nor rpc-reply.",
+ iter->schema->name);
+ return LY_EINVAL;
+ } else if ((iter->schema->nodetype == LYS_ACTION) && !(int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY))) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent action \"%s\" node when not parsing action nor rpc-reply.",
+ iter->schema->name);
+ return LY_EINVAL;
+ } else if ((iter->schema->nodetype == LYS_NOTIF) && !(int_opts & LYD_INTOPT_NOTIF)) {
+ LOGERR(LYD_CTX(parent), LY_EINVAL, "Invalid parent notification \"%s\" node when not parsing a notification.",
+ iter->schema->name);
+ return LY_EINVAL;
+ }
+
+ *op = (struct lyd_node *)iter;
+ return LY_SUCCESS;
+}
+
+LY_ERR
lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
{
- LY_ERR ret = LY_EVALID;
+ LY_ERR rc = LY_SUCCESS;
LOG_LOCSET(snode, NULL, NULL, NULL);
- if ((lydctx->parse_options & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
+ if ((lydctx->parse_opts & LYD_PARSE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
LOGVAL(lydctx->data_ctx->ctx, LY_VCODE_INNODE, "state", snode->name);
+ rc = LY_EVALID;
goto cleanup;
}
- if (snode->nodetype & (LYS_RPC | LYS_ACTION)) {
+ if (snode->nodetype == LYS_RPC) {
if (lydctx->int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY)) {
if (lydctx->op_node) {
- LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
- lys_nodetype2str(snode->nodetype), snode->name,
- lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
- goto cleanup;
+ goto error_node_dup;
}
} else {
- LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\".",
- lys_nodetype2str(snode->nodetype), snode->name);
- goto cleanup;
+ goto error_node_inval;
+ }
+ } else if (snode->nodetype == LYS_ACTION) {
+ if (lydctx->int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) {
+ if (lydctx->op_node) {
+ goto error_node_dup;
+ }
+ } else {
+ goto error_node_inval;
}
} else if (snode->nodetype == LYS_NOTIF) {
if (lydctx->int_opts & LYD_INTOPT_NOTIF) {
if (lydctx->op_node) {
- LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
- lys_nodetype2str(snode->nodetype), snode->name,
- lys_nodetype2str(lydctx->op_node->schema->nodetype), lydctx->op_node->schema->name);
- goto cleanup;
+ goto error_node_dup;
}
} else {
- LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\".",
- lys_nodetype2str(snode->nodetype), snode->name);
- goto cleanup;
+ goto error_node_inval;
}
}
- ret = LY_SUCCESS;
+ /* success */
+ goto cleanup;
+
+error_node_dup:
+ LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
+ lys_nodetype2str(snode->nodetype), snode->name, lys_nodetype2str(lydctx->op_node->schema->nodetype),
+ lydctx->op_node->schema->name);
+ rc = LY_EVALID;
+ goto cleanup;
+
+error_node_inval:
+ LOGVAL(lydctx->data_ctx->ctx, LYVE_DATA, "Unexpected %s element \"%s\".", lys_nodetype2str(snode->nodetype),
+ snode->name);
+ rc = LY_EVALID;
cleanup:
LOG_LOCBACK(1, 0, 0, 0);
- return ret;
+ return rc;
}
LY_ERR
@@ -435,7 +493,7 @@
LY_CHECK_RET(lyd_create_term(schema, value, value_len, dynamic, format, prefix_data, hints, &incomplete, node));
- if (incomplete && !(lydctx->parse_options & LYD_PARSE_ONLY)) {
+ if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
LY_CHECK_RET(ly_set_add(&lydctx->node_types, *node, 1, NULL));
}
return LY_SUCCESS;
@@ -457,7 +515,7 @@
LY_CHECK_RET(lyd_create_meta(parent, meta, mod, name, name_len, value, value_len, dynamic, format, prefix_data,
hints, 0, &incomplete));
- if (incomplete && !(lydctx->parse_options & LYD_PARSE_ONLY)) {
+ if (incomplete && !(lydctx->parse_opts & LYD_PARSE_ONLY)) {
LY_CHECK_RET(ly_set_add(&lydctx->meta_types, *meta, 1, NULL));
}
diff --git a/src/lyb.h b/src/lyb.h
index bb72f0b..85ad901 100644
--- a/src/lyb.h
+++ b/src/lyb.h
@@ -56,17 +56,17 @@
struct lyd_lyb_ctx {
union {
struct {
- uint32_t parse_options; /**< various @ref dataparseroptions. */
- uint32_t validate_options; /**< various @ref datavalidationoptions. */
+ uint32_t parse_opts; /**< various @ref dataparseroptions. */
+ uint32_t val_opts; /**< various @ref datavalidationoptions. */
};
uint32_t print_options;
};
uint32_t int_opts; /**< internal data parser options */
uint32_t path_len; /**< used bytes in the path buffer */
char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
- struct ly_set unres_node_type; /**< set of nodes validated with LY_EINCOMPLETE result */
- struct ly_set unres_meta_type; /**< set of metadata validated with LY_EINCOMPLETE result */
- struct ly_set when_check; /**< set of nodes with "when" conditions */
+ struct ly_set node_when; /**< set of nodes with "when" conditions */
+ struct ly_set node_types; /**< set of nodes validated with LY_EINCOMPLETE result */
+ struct ly_set meta_types; /**< set of metadata validated with LY_EINCOMPLETE result */
struct lyd_node *op_node; /**< if an RPC/action/notification is being parsed, store the pointer to it */
/* callbacks */
diff --git a/src/parser_data.h b/src/parser_data.h
index f6327ea..d32aaff 100644
--- a/src/parser_data.h
+++ b/src/parser_data.h
@@ -58,10 +58,8 @@
* example the *:operational* datastore is not necessarily valid and results of the NETCONF's \<get\> or \<get-config\>
* oprations used with filters will be incomplete (and thus invalid). This can be allowed using ::LYD_PARSE_ONLY,
* the ::LYD_PARSE_NO_STATE should be used for the data returned by \<get-config\> operation.
- * - ::lyd_parse_rpc() is used for parsing RPCs/Actions, optionally including the RPC envelopes known from the NETCONF
- * protocol.
- * - ::lyd_parse_reply() processes reply to a previous RPC/Action, which must be provided.
- * - ::lyd_parse_notif() is able to process complete Notification message.
+ * - ::lyd_parse_op() is used for parsing RPCs/actions, replies, and notifications. Even NETCONF rpc, rpc-reply, and
+ * notification messages are supported.
*
* Further information regarding the processing input instance data can be found on the following pages.
* - @subpage howtoDataValidation
@@ -73,9 +71,7 @@
* - ::lyd_parse_data_mem()
* - ::lyd_parse_data_fd()
* - ::lyd_parse_data_path()
- * - ::lyd_parse_rpc()
- * - ::lyd_parse_reply()
- * - ::lyd_parse_notif()
+ * - ::lyd_parse_op()
*/
/**
@@ -188,52 +184,32 @@
/** @} datavalidationoptions */
/**
- * @ingroup datatree
- * @defgroup datavalidateop Operation to validate
- *
- * Operation provided to ::lyd_validate_op() to validate.
- *
- * The operation cannot be determined automatically since RPC/action and a reply to it share the common top level node
- * referencing the RPC/action schema node and may not have any input/output children to use for distinction.
- *
- * @{
- */
-typedef enum {
- LYD_VALIDATE_OP_RPC = 1, /**< Validate RPC/action request (input parameters). */
- LYD_VALIDATE_OP_REPLY, /**< Validate RPC/action reply (output parameters). */
- LYD_VALIDATE_OP_NOTIF /**< Validate Notification operation. */
-} LYD_VALIDATE_OP;
-
-/** @} datavalidateop */
-
-/**
* @brief Parse (and validate) data from the input handler as a YANG data tree.
*
* @param[in] ctx Context to connect with the tree being built here.
+ * @param[in] parent Optional parent to connect the parsed nodes to.
* @param[in] in The input handle to provide the dumped data in the specified @p format to parse (and validate).
* @param[in] format Format of the input data to be parsed. Can be 0 to try to detect format from the input handler.
* @param[in] parse_options Options for parser, see @ref dataparseroptions.
* @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[out] tree Resulting data tree built from the input data. Note that NULL can be a valid result as a representation of an empty YANG data tree.
- * The returned data are expected to be freed using ::lyd_free_all().
+ * @param[out] tree Full parsed data tree, note that NULL can be a valid tree. If @p parent is set, set to NULL.
* @return LY_SUCCESS in case of successful parsing (and validation).
* @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
*/
-LY_ERR lyd_parse_data(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, uint32_t parse_options,
- uint32_t validate_options, struct lyd_node **tree);
+LY_ERR lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+ uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree);
/**
* @brief Parse (and validate) input data as a YANG data tree.
*
- * Wrapper around ::lyd_parse_data() hiding work with the input handler.
+ * Wrapper around ::lyd_parse_data() hiding work with the input handler and some obscure options.
*
* @param[in] ctx Context to connect with the tree being built here.
* @param[in] data The input data in the specified @p format to parse (and validate).
* @param[in] format Format of the input data to be parsed. Can be 0 to try to detect format from the input handler.
* @param[in] parse_options Options for parser, see @ref dataparseroptions.
* @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[out] tree Resulting data tree built from the input data. Note that NULL can be a valid result as a representation of an empty YANG data tree.
- * The returned data are expected to be freed using ::lyd_free_all().
+ * @param[out] tree Full parsed data tree, note that NULL can be a valid tree
* @return LY_SUCCESS in case of successful parsing (and validation).
* @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
*/
@@ -243,33 +219,32 @@
/**
* @brief Parse (and validate) input data as a YANG data tree.
*
- * Wrapper around ::lyd_parse_data() hiding work with the input handler.
+ * Wrapper around ::lyd_parse_data() hiding work with the input handler and some obscure options.
*
* @param[in] ctx Context to connect with the tree being built here.
- * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the input data in the specified @p format to parse (and validate).
+ * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the input data in the
+ * specified @p format to parse.
* @param[in] format Format of the input data to be parsed. Can be 0 to try to detect format from the input handler.
* @param[in] parse_options Options for parser, see @ref dataparseroptions.
* @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[out] tree Resulting data tree built from the input data. Note that NULL can be a valid result as a representation of an empty YANG data tree.
- * The returned data are expected to be freed using ::lyd_free_all().
+ * @param[out] tree Full parsed data tree, note that NULL can be a valid tree
* @return LY_SUCCESS in case of successful parsing (and validation).
* @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
*/
-LY_ERR lyd_parse_data_fd(const struct ly_ctx *ctx, int fd, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options,
- struct lyd_node **tree);
+LY_ERR lyd_parse_data_fd(const struct ly_ctx *ctx, int fd, LYD_FORMAT format, uint32_t parse_options,
+ uint32_t validate_options, struct lyd_node **tree);
/**
* @brief Parse (and validate) input data as a YANG data tree.
*
- * Wrapper around ::lyd_parse_data() hiding work with the input handler.
+ * Wrapper around ::lyd_parse_data() hiding work with the input handler and some obscure options.
*
* @param[in] ctx Context to connect with the tree being built here.
* @param[in] path Path to the file with the input data in the specified @p format to parse (and validate).
* @param[in] format Format of the input data to be parsed. Can be 0 to try to detect format from the input handler.
* @param[in] parse_options Options for parser, see @ref dataparseroptions.
* @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[out] tree Resulting data tree built from the input data. Note that NULL can be a valid result as a representation of an empty YANG data tree.
- * The returned data are expected to be freed using ::lyd_free_all().
+ * @param[out] tree Full parsed data tree, note that NULL can be a valid tree
* @return LY_SUCCESS in case of successful parsing (and validation).
* @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
*/
@@ -277,51 +252,66 @@
uint32_t validate_options, struct lyd_node **tree);
/**
- * @brief Parse (no validation) data from the input handler as a YANG RPC/action request.
+ * @ingroup datatree
+ * @defgroup datatype Data operation type
*
- * @param[in] ctx Context to connect with the tree being parsed.
- * @param[in] in Input handle to provide the dumped data in the specified @p format to parse.
- * @param[in] format Format of the input data to be parsed.
- * @param[out] tree Resulting full RPC/action request tree built from the input data. The returned data are expected to be freed using ::lyd_free_all().
- * In contrast to YANG data tree, result of parsing RPC/action cannot be NULL until an error occurs.
- * @param[out] op Optional pointer to the actual operation node inside the full action @p tree, useful only for action.
- * @return LY_SUCCESS in case of successful parsing.
- * @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
+ * Operation provided to ::lyd_validate_op() to validate.
+ *
+ * The operation cannot be determined automatically since RPC/action and a reply to it share the common top level node
+ * referencing the RPC/action schema node and may not have any input/output children to use for distinction.
+ *
+ * @{
*/
-LY_ERR lyd_parse_rpc(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree,
- struct lyd_node **op);
+enum lyd_type {
+ LYD_TYPE_YANG_DATA = 0, /* generic YANG instance data */
+ LYD_TYPE_YANG_RPC, /* instance of a YANG RPC/action request with only "input" data children,
+ including all parents in case of an action */
+ LYD_TYPE_YANG_NOTIF, /* instance of a YANG notification , including all parents in case of a nested one */
+ LYD_TYPE_YANG_REPLY, /* instance of a YANG RPC/action reply with only "output" data children,
+ including all parents in case of an action */
+ LYD_TYPE_NETCONF_RPC, /* complete NETCONF RPC invocation as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
+ LYD_TYPE_NETCONF_REPLY_OR_NOTIF /* complete NETCONF RPC reply as defined for
+ [RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
+ [action](https://tools.ietf.org/html/rfc7950#section-7.15.2) or
+ [notification](https://tools.ietf.org/html/rfc7950#section-7.16.2), which
+ of these is parsed is decided based on the first element name */
+};
+/** @} datatype */
/**
- * @brief Parse (no validation) data from the input handler as a YANG RPC/action reply.
+ * @brief Parse YANG data into an operation data tree.
*
- * @param[in] ctx Context to connect with the tree being parsed.
- * @param[in] in Input handle to provide the dumped data in the specified @p format to parse (and validate).
- * @param[in] format Format of the input data to be parsed.
- * @param[out] tree Resulting full RPC/action reply tree built from the input data. The returned data are expected to be freed using ::lyd_free_all().
- * In contrast to YANG data tree, result of parsing RPC/action cannot be NULL until an error occurs.
- * At least one of the @p tree and @p op output variables must be provided.
- * @param[out] op Pointer to the actual operation node inside the full action reply @p tree, useful only for action. At least one of the @p op
- * and @p tree output variables must be provided.
- * @return LY_SUCCESS in case of successful parsing.
- * @return LY_ERR value in case of error. Additional error information can be obtained from the request's context using ly_err* functions.
- */
-LY_ERR lyd_parse_reply(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree,
- struct lyd_node **op);
-
-/**
- * @brief Parse (no validation) data from the input handler as a YANG notification.
+ * At least one of @p parent, @p tree, or @p op must always be set.
*
- * @param[in] ctx Context to connect with the tree being parsed.
- * @param[in] in Input handle to provide the dumped data in the specified @p format to parse (and validate).
- * @param[in] format Format of the input data to be parsed.
- * @param[out] tree Resulting full notification tree built from the input data. The returned data are expected to be freed using ::lyd_free_all().
- * In contrast to YANG data tree, result of parsing notification cannot be NULL until an error occurs.
- * @param[out] ntf Optional pointer to the actual notification node inside the full notification @p tree, useful for nested notifications.
- * @return LY_SUCCESS in case of successful parsing.
- * @return LY_ERR value in case of error. Additional error information can be obtained from the context using ly_err* functions.
+ * Specific @p data_type values have different parameter meaning as follows:
+ * - ::LYD_TYPE_NETCONF_RPC:
+ * - @p parent - must be NULL, the whole RPC is expected;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree;
+ * - @p op - must be provided, the RPC/action data tree itself will be returned here, pointing to the operation;
+ *
+ * - ::LYD_TYPE_NETCONF_REPLY_OR_NOTIF:
+ * - @p parent - must be set, pointing to the invoked RPC operation (RPC or action) node;
+ * - @p format - must be ::LYD_XML, NETCONF supports only this format;
+ * - @p tree - must be provided, all the NETCONF-specific XML envelopes will be returned here as
+ * a separate opaque data tree;
+ * - @p op - must be provided, the notification data tree itself will be returned here, pointing to the operation,
+ * it will be set to NULL if no data nodes were parsed in the reply (ok or rpc-error);
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] parent Optional parent to connect the parsed nodes to.
+ * @param[in] in Input handle to read the input from.
+ * @param[in] format Expected format of the data in @p in.
+ * @param[in] data_type Expected operation to parse (@ref datatype).
+ * @param[out] tree Optional full parsed data tree. If @p parent is set, set to NULL.
+ * @param[out] op Optional parsed operation node.
+ * @return LY_ERR value.
*/
-LY_ERR lyd_parse_notif(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree,
- struct lyd_node **ntf);
+LY_ERR lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+ enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op);
/**
* @brief Fully validate a data tree.
@@ -348,20 +338,18 @@
LY_ERR lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, uint32_t val_opts, struct lyd_node **diff);
/**
- * @brief Validate an RPC/action, notification, or RPC/action reply.
+ * @brief Validate an RPC/action request, reply, or notification.
*
* @param[in,out] op_tree Operation tree with any parents. It can point to the operation itself or any of
* its parents, only the operation subtree is actually validated.
- * @param[in] tree Tree to be used for validating references from the operation subtree.
- * @param[in] op Operation to validate (@ref datavalidateop), the given @p op_tree must correspond to this value. Note that
- * it isn't possible to detect the operation simply from the @p op_tree since RPC/action and their reply share the same
- * RPC/action data node and in case one of the input and output do not define any data node children, it is not possible
- * to get know what is here given for validation and if it is really valid.
+ * @param[in] dep_tree Tree to be used for validating references from the operation subtree.
+ * @param[in] data_type Operation type to validate (only YANG operations are accepted, @ref datatype).
* @param[out] diff Optional diff with any changes made by the validation.
* @return LY_SUCCESS on success.
* @return LY_ERR error on error.
*/
-LY_ERR lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *tree, LYD_VALIDATE_OP op, struct lyd_node **diff);
+LY_ERR lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *dep_tree, enum lyd_type data_type,
+ struct lyd_node **diff);
/** @} datatree */
diff --git a/src/parser_internal.h b/src/parser_internal.h
index 408433d..be99884 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -15,6 +15,7 @@
#ifndef LY_PARSER_INTERNAL_H_
#define LY_PARSER_INTERNAL_H_
+#include "parser_data.h"
#include "plugins_types.h"
#include "tree_schema_internal.h"
@@ -29,12 +30,22 @@
typedef void (*lyd_ctx_free_clb)(struct lyd_ctx *ctx);
/**
+ * @brief Internal data parser flags.
+ */
+#define LYD_INTOPT_RPC 0x01 /**< RPC request is being parsed. */
+#define LYD_INTOPT_ACTION 0x02 /**< Action request is being parsed. */
+#define LYD_INTOPT_REPLY 0x04 /**< RPC/action reply is being parsed. */
+#define LYD_INTOPT_NOTIF 0x08 /**< Notification is being parsed. */
+#define LYD_INTOPT_WITH_SIBLINGS 0x10 /**< Parse the whole input with any siblings. */
+#define LYD_INTOPT_NO_SIBLINGS 0x20 /**< If there are any siblings, return an error. */
+
+/**
* @brief Internal (common) context for YANG data parsers.
*/
struct lyd_ctx {
- uint32_t parse_options; /**< various @ref dataparseroptions. */
- uint32_t validate_options; /**< various @ref datavalidationoptions. */
- uint32_t int_opts; /**< internal data parser options */
+ uint32_t parse_opts; /**< various @ref dataparseroptions. */
+ uint32_t val_opts; /**< various @ref datavalidationoptions. */
+ uint32_t int_opts; /**< internal parser options */
uint32_t path_len; /**< used bytes in the path buffer */
#define LYD_PARSER_BUFSIZE 4078
char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
@@ -109,145 +120,67 @@
struct ly_in *in, struct lysp_submodule **submod);
/**
- * @brief Parse XML string as YANG data tree.
+ * @brief Parse XML string as a YANG data tree.
*
- * @param[in] ctx libyang context
+ * @param[in] ctx libyang context.
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
* @param[in] in Input structure.
- * @param[in] parse_options Options for parser, see @ref dataparseroptions.
- * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[out] tree_p Parsed data tree. Note that NULL can be a valid result.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] data_type Expected data type of the data.
+ * @param[out] envp Individual parsed envelopes tree, returned only by specific @p data_type.
+ * @param[out] parsed Set to add all the parsed siblings into.
* @param[out] lydctx_p Data parser context to finish validation.
* @return LY_ERR value.
*/
-LY_ERR lyd_parse_xml_data(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_options, uint32_t validate_options,
- struct lyd_node **tree_p, struct lyd_ctx **lydctx_p);
+LY_ERR lyd_parse_xml(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in,
+ uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, struct lyd_node **envp, struct ly_set *parsed,
+ struct lyd_ctx **lydctx_p);
/**
- * @brief Parse XML string as YANG RPC/action invocation.
+ * @brief Parse JSON string as a YANG data tree.
*
* @param[in] ctx libyang context.
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
* @param[in] in Input structure.
- * @param[out] tree_p Parsed full RPC/action tree.
- * @param[out] op_p Optional pointer to the actual operation. Useful mainly for action.
- * @return LY_ERR value.
- */
-LY_ERR lyd_parse_xml_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
-
-/**
- * @brief Parse XML string as YANG notification.
- *
- * @param[in] ctx libyang context.
- * @param[in] in Input structure.
- * @param[out] tree_p Parsed full notification tree.
- * @param[out] op_p Optional pointer to the actual notification. Useful mainly for nested notifications.
- * @return LY_ERR value.
- */
-LY_ERR lyd_parse_xml_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p);
-
-/**
- * @brief Parse XML string as YANG RPC/action reply.
- *
- * @param[in] ctx libyang context.
- * @param[in] in Input structure.
- * @param[out] tree_p Parsed full RPC/action reply tree.
- * @param[out] op_p Optional pointer to the reply operation. Useful mainly for action.
- * @return LY_ERR value.
- */
-LY_ERR lyd_parse_xml_reply(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
-
-/**
- * @brief Parse JSON string as YANG data tree.
- *
- * @param[in] ctx libyang context
- * @param[in] in Input structure.
- * @param[in] parse_options Options for parser, see @ref dataparseroptions.
- * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[out] tree_p Parsed data tree. Note that NULL can be a valid result.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] data_type Expected data type of the data.
+ * @param[out] parsed Set to add all the parsed siblings into.
* @param[out] lydctx_p Data parser context to finish validation.
* @return LY_ERR value.
*/
-LY_ERR lyd_parse_json_data(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_options, uint32_t validate_options,
- struct lyd_node **tree_p, struct lyd_ctx **lydctx_p);
+LY_ERR lyd_parse_json(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in,
+ uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, struct ly_set *parsed, struct lyd_ctx **lydctx_p);
/**
- * @brief Parse JSON string as YANG notification.
+ * @brief Parse binary LYB data as a YANG data tree.
*
* @param[in] ctx libyang context.
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
* @param[in] in Input structure.
- * @param[out] tree_p Parsed full notification tree.
- * @param[out] ntf_p Optional pointer to the actual notification. Useful mainly for nested notifications.
- * @return LY_ERR value.
- */
-LY_ERR lyd_parse_json_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p);
-
-/**
- * @brief Parse JSON string as YANG RPC/action invocation.
- *
- * @param[in] ctx libyang context.
- * @param[in] in Input structure.
- * @param[out] tree_p Parsed full RPC/action tree.
- * @param[out] op_p Optional pointer to the actual operation. Useful mainly for action.
- * @return LY_ERR value.
- */
-LY_ERR lyd_parse_json_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
-
-/**
- * @brief Parse JSON string as YANG RPC/action reply.
- *
- * @param[in] ctx libyang context.
- * @param[in] in Input structure.
- * @param[out] tree_p Parsed full RPC/action reply tree.
- * @param[out] op_p Optional pointer to the reply operation. Useful mainly for action.
- * @return LY_ERR value.
- */
-LY_ERR lyd_parse_json_reply(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
-
-/**
- * @brief Parse binary data as YANG data tree.
- *
- * @param[in] ctx libyang context
- * @param[in] in Input structure.
- * @param[in] parse_options Options for parser, see @ref dataparseroptions.
- * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[out] tree_p Parsed data tree. Note that NULL can be a valid result.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] data_type Expected data type of the data.
+ * @param[out] parsed Set to add all the parsed siblings into.
* @param[out] lydctx_p Data parser context to finish validation.
* @return LY_ERR value.
*/
-LY_ERR lyd_parse_lyb_data(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_options, uint32_t validate_options,
- struct lyd_node **tree_p, struct lyd_ctx **lydctx_p);
+LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in,
+ uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, struct ly_set *parsed, struct lyd_ctx **lydctx_p);
/**
- * @brief Parse binary data as YANG RPC/action invocation.
+ * @brief Search all the parents for an operation node, check validity based on internal parser flags.
*
- * @param[in] ctx libyang context.
- * @param[in] in Input structure.
- * @param[out] tree_p Parsed full RPC/action tree.
- * @param[out] op_p Optional pointer to the actual operation. Useful mainly for action.
+ * @param[in] parent Parent to connect the parsed nodes to.
+ * @param[in] int_opt Internal parser options.
+ * @param[out] op Found operation, if any.
* @return LY_ERR value.
*/
-LY_ERR lyd_parse_lyb_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
-
-/**
- * @brief Parse binary data as YANG notification.
- *
- * @param[in] ctx libyang context.
- * @param[in] in Input structure.
- * @param[out] tree_p Parsed full notification tree.
- * @param[out] ntf_p Optional pointer to the actual notification. Useful mainly for nested notifications.
- * @return LY_ERR value.
- */
-LY_ERR lyd_parse_lyb_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p);
-
-/**
- * @brief Parse binary data as YANG RPC/action reply.
- *
- * @param[in] ctx libyang context.
- * @param[in] in Input structure.
- * @param[out] tree_p Parsed full RPC/action reply tree.
- * @param[out] op_p Optional pointer to the reply operation. Useful mainly for action.
- * @return LY_ERR value.
- */
-LY_ERR lyd_parse_lyb_reply(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p);
+LY_ERR lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op);
/**
* @brief Check that a data node representing the @p snode is suitable based on options.
diff --git a/src/parser_json.c b/src/parser_json.c
index 8fd2f06..bccd286 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -40,8 +40,8 @@
* Note that the structure maps to the lyd_ctx which is common for all the data parsers
*/
struct lyd_json_ctx {
- uint32_t parse_options; /**< various @ref dataparseroptions. */
- uint32_t validate_options; /**< various @ref datavalidationoptions. */
+ uint32_t parse_opts; /**< various @ref dataparseroptions. */
+ uint32_t val_opts; /**< various @ref datavalidationoptions. */
uint32_t int_opts; /**< internal data parser options */
uint32_t path_len; /**< used bytes in the path buffer */
char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
@@ -214,12 +214,12 @@
goto cleanup;
}
if (!mod) {
- if (lydctx->parse_options & LYD_PARSE_STRICT) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", prefix_len, prefix);
ret = LY_EVALID;
goto cleanup;
}
- if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
+ if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
ret = LY_ENOT;
goto cleanup;
}
@@ -229,12 +229,12 @@
if (mod && (!parent || parent->schema)) {
*snode_p = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
if (!*snode_p) {
- if (lydctx->parse_options & LYD_PARSE_STRICT) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
name_len, name, mod->name);
ret = LY_EVALID;
goto cleanup;
- } else if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
+ } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
/* skip element with children */
ret = LY_ENOT;
goto cleanup;
@@ -435,7 +435,7 @@
return LY_SUCCESS;
}
- if (lydctx->parse_options & LYD_PARSE_OPAQ) {
+ if (lydctx->parse_opts & LYD_PARSE_OPAQ) {
/* backup parser */
lyjson_ctx_backup(jsonctx);
status = lyjson_ctx_status(jsonctx, 0);
@@ -585,7 +585,7 @@
meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value),
&dynamic, LY_PREF_JSON, NULL, meta->hints);
LY_CHECK_GOTO(ret, cleanup);
- } else if (lydctx->parse_options & LYD_PARSE_STRICT) {
+ } else if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE,
"Unknown (or not implemented) YANG module \"%s\" for metadata \"%s%s%s\".",
meta->name.prefix, meta->name.prefix, ly_strlen(meta->name.prefix) ? ":" : "", meta->name.name);
@@ -594,7 +594,7 @@
}
}
/* add/correct flags */
- lyd_parse_set_data_flags(node, &lydctx->node_when, &node->meta, lydctx->parse_options);
+ lyd_parse_set_data_flags(node, &lydctx->node_when, &node->meta, lydctx->parse_opts);
/* done */
break;
@@ -743,13 +743,13 @@
/* get the element module */
mod = ly_ctx_get_module_implemented2(ctx, prefix, prefix_len);
if (!mod) {
- if (lydctx->parse_options & LYD_PARSE_STRICT) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(ctx, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.",
prefix_len, prefix, name_len, name);
ret = LY_EVALID;
goto cleanup;
}
- if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
+ if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
/* skip element with children */
ret = lydjson_data_skip(lydctx->jsonctx);
LY_CHECK_GOTO(ret, cleanup);
@@ -772,7 +772,7 @@
LY_CHECK_GOTO(ret, cleanup);
/* add/correct flags */
- lyd_parse_set_data_flags(node, &lydctx->node_when, &meta, lydctx->parse_options);
+ lyd_parse_set_data_flags(node, &lydctx->node_when, &meta, lydctx->parse_opts);
} else {
/* create attribute */
const char *module_name;
@@ -845,7 +845,8 @@
}
}
-static LY_ERR lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node_inner *parent, struct lyd_node **first_p);
+static LY_ERR lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p,
+ struct ly_set *parsed);
/**
* @brief Parse opaq node from the input.
@@ -900,7 +901,7 @@
if ((*status_p == LYJSON_OBJECT) || (*status_p == LYJSON_OBJECT_EMPTY)) {
/* process children */
while (*status_p != LYJSON_OBJECT_CLOSED && *status_p != LYJSON_OBJECT_EMPTY) {
- LY_CHECK_RET(lydjson_subtree_r(lydctx, (struct lyd_node_inner *)(*node_p), lyd_node_children_p(*node_p)));
+ LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
*status_p = lyjson_ctx_status(lydctx->jsonctx, 0);
}
} else if ((*status_p == LYJSON_ARRAY) || (*status_p == LYJSON_ARRAY_EMPTY)) {
@@ -911,7 +912,7 @@
if ((*status_inner_p == LYJSON_OBJECT) || (*status_inner_p == LYJSON_OBJECT_EMPTY)) {
/* but first process children of the object in the array */
while (*status_inner_p != LYJSON_OBJECT_CLOSED && *status_inner_p != LYJSON_OBJECT_EMPTY) {
- LY_CHECK_RET(lydjson_subtree_r(lydctx, (struct lyd_node_inner *)(*node_p), lyd_node_children_p(*node_p)));
+ LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
*status_inner_p = lyjson_ctx_status(lydctx->jsonctx, 0);
}
}
@@ -922,12 +923,13 @@
if (*status_inner_p != LYJSON_ARRAY_CLOSED) {
assert(node_p);
lydjson_maintain_children(parent, first_p, node_p);
- return lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_p, status_inner_p, first_p, node_p);
+ return lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_p, status_inner_p,
+ first_p, node_p);
}
}
/* finish linking metadata */
- LY_CHECK_RET(lydjson_metadata_finish(lydctx, lyd_node_children_p(*node_p)));
+ LY_CHECK_RET(lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p)));
/* move after the item */
return lyjson_ctx_next(lydctx->jsonctx, status_p);
@@ -953,9 +955,8 @@
*/
static LY_ERR
lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, const struct lysc_node *snode,
- const char *name, size_t name_len, const char *prefix, size_t prefix_len,
- struct lyd_node_inner *parent, enum LYJSON_PARSER_STATUS *status_p, struct lyd_node **first_p,
- struct lyd_node **node_p)
+ const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent,
+ enum LYJSON_PARSER_STATUS *status_p, struct lyd_node **first_p, struct lyd_node **node_p)
{
LY_ERR ret = LY_SUCCESS;
enum LYJSON_PARSER_STATUS status_inner;
@@ -988,15 +989,16 @@
}
/* backup parser options to parse unknown metadata as opaq nodes and try to resolve them later */
- prev_opts = lydctx->parse_options;
- lydctx->parse_options &= ~LYD_PARSE_STRICT;
- lydctx->parse_options |= LYD_PARSE_OPAQ;
+ prev_opts = lydctx->parse_opts;
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ lydctx->parse_opts |= LYD_PARSE_OPAQ;
ret = lydjson_parse_opaq(lydctx, prefix ? prefix - 1 : name - 1, prefix ? prefix_len + name_len + 2 : name_len + 1,
- NULL, 0, parent, status_p, status_inner == LYJSON_ERROR ? status_p : &status_inner, first_p, node_p);
+ NULL, 0, (struct lyd_node_inner *)parent, status_p, status_inner == LYJSON_ERROR ? status_p : &status_inner,
+ first_p, node_p);
/* restore the parser options */
- lydctx->parse_options = prev_opts;
+ lydctx->parse_opts = prev_opts;
} else {
ret = lydjson_metadata(lydctx, snode, attr_node);
}
@@ -1056,13 +1058,13 @@
/* process children */
while (*status != LYJSON_OBJECT_CLOSED && *status != LYJSON_OBJECT_EMPTY) {
- ret = lydjson_subtree_r(lydctx, (struct lyd_node_inner *)*node, lyd_node_children_p(*node));
+ ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL);
LY_CHECK_ERR_RET(ret, LOG_LOCBACK(1, 1, 0, 0), ret);
*status = lyjson_ctx_status(lydctx->jsonctx, 0);
}
/* finish linking metadata */
- ret = lydjson_metadata_finish(lydctx, lyd_node_children_p(*node));
+ ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node));
LY_CHECK_ERR_RET(ret, LOG_LOCBACK(1, 1, 0, 0), ret);
if (snode->nodetype == LYS_LIST) {
@@ -1071,14 +1073,14 @@
LY_CHECK_ERR_RET(ret, LOG_LOCBACK(1, 1, 0, 0), ret);
}
- if (!(lydctx->parse_options & LYD_PARSE_ONLY)) {
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
/* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_children_p(*node), snode, NULL, NULL);
+ ret = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, NULL);
LY_CHECK_ERR_RET(ret, LOG_LOCBACK(1, 1, 0, 0), ret);
/* add any missing default children */
- ret = lyd_new_implicit_r(*node, lyd_node_children_p(*node), NULL, NULL, &lydctx->node_types,
- &lydctx->node_when, (lydctx->validate_options & LYD_VALIDATE_NO_STATE) ?
+ ret = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_types,
+ &lydctx->node_when, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ?
LYD_IMPLICIT_NO_STATE : 0, NULL);
LY_CHECK_ERR_RET(ret, LOG_LOCBACK(1, 1, 0, 0), ret);
}
@@ -1094,19 +1096,19 @@
/* parse any data tree with correct options */
/* first backup the current options and then make the parser to process data as opaq nodes */
- prev_opts = lydctx->parse_options;
- lydctx->parse_options &= ~LYD_PARSE_STRICT;
- lydctx->parse_options |= LYD_PARSE_OPAQ;
+ prev_opts = lydctx->parse_opts;
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ lydctx->parse_opts |= LYD_PARSE_OPAQ;
/* process the anydata content */
while (*status != LYJSON_OBJECT_CLOSED && *status != LYJSON_OBJECT_EMPTY) {
- ret = lydjson_subtree_r(lydctx, NULL, &tree);
+ ret = lydjson_subtree_r(lydctx, NULL, &tree, NULL);
LY_CHECK_RET(ret);
*status = lyjson_ctx_status(lydctx->jsonctx, 0);
}
/* restore parser options */
- lydctx->parse_options = prev_opts;
+ lydctx->parse_opts = prev_opts;
/* finish linking metadata */
ret = lydjson_metadata_finish(lydctx, &tree);
@@ -1137,10 +1139,11 @@
* @param[in] lydctx JSON data parser context.
* @param[in] parent Data parent of the subtree, must be set if @p first is not.
* @param[in,out] first_p Pointer to the variable holding the first top-level sibling, must be set if @p parent is not.
+ * @param[in,out] parsed Optional set to add all the parsed siblings into.
* @return LY_ERR value.
*/
static LY_ERR
-lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node_inner *parent, struct lyd_node **first_p)
+lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
{
LY_ERR ret = LY_SUCCESS;
enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx, 0);
@@ -1161,7 +1164,7 @@
if (!is_meta || name_len || prefix_len) {
/* get the schema node */
- ret = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode);
+ ret = lydjson_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, (struct lyd_node_inner *)parent, &snode);
if (ret == LY_ENOT) {
/* skip element with children */
ret = lydjson_data_skip(lydctx->jsonctx);
@@ -1188,7 +1191,7 @@
ret = LY_EVALID;
goto cleanup;
}
- attr_node = &parent->node;
+ attr_node = parent;
snode = attr_node->schema;
}
ret = lydjson_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, &status,
@@ -1196,7 +1199,7 @@
LY_CHECK_GOTO(ret, cleanup);
} else if (!snode) {
/* parse as an opaq node */
- assert((lydctx->parse_options & LYD_PARSE_OPAQ) || (lydctx->int_opts));
+ assert((lydctx->parse_opts & LYD_PARSE_OPAQ) || (lydctx->int_opts));
/* move to the second item in the name/X pair */
ret = lyjson_ctx_next(lydctx->jsonctx, &status);
@@ -1211,8 +1214,8 @@
status_inner = LYJSON_ERROR;
}
- ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len,
- parent, &status, status_inner == LYJSON_ERROR ? &status : &status_inner, first_p, &node);
+ ret = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, (struct lyd_node_inner *)parent, &status,
+ status_inner == LYJSON_ERROR ? &status : &status_inner, first_p, &node);
LY_CHECK_GOTO(ret, cleanup);
} else {
/* parse as a standard lyd_node but it can still turn out to be an opaque node */
@@ -1239,10 +1242,10 @@
/* process all the values/objects */
do {
- lydjson_maintain_children(parent, first_p, &node);
+ lydjson_maintain_children((struct lyd_node_inner *)parent, first_p, &node);
- ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len,
- &status, &node);
+ ret = lydjson_parse_instance(lydctx, (struct lyd_node_inner *)parent, first_p, snode, name, name_len,
+ prefix, prefix_len, &status, &node);
if (ret == LY_EINVAL) {
goto representation_error;
} else if (ret) {
@@ -1273,7 +1276,8 @@
}
/* process the value/object */
- ret = lydjson_parse_instance(lydctx, parent, first_p, snode, name, name_len, prefix, prefix_len, &status, &node);
+ ret = lydjson_parse_instance(lydctx, (struct lyd_node_inner *)parent, first_p, snode, name, name_len,
+ prefix, prefix_len, &status, &node);
if (ret == LY_EINVAL) {
goto representation_error;
} else if (ret) {
@@ -1290,13 +1294,18 @@
}
/* finally connect the parsed node */
- lydjson_maintain_children(parent, first_p, &node);
+ lydjson_maintain_children((struct lyd_node_inner *)parent, first_p, &node);
+
+ /* rememeber a successfully parsed node */
+ if (parsed) {
+ ly_set_add(parsed, node, 1, NULL);
+ }
/* success */
goto cleanup;
representation_error:
- LOG_LOCSET(NULL, &parent->node, NULL, NULL);
+ LOG_LOCSET(NULL, parent, NULL, NULL);
LOGVAL(ctx, LYVE_SYNTAX_JSON, "The %s \"%s\" is expected to be represented as JSON %s, but input data contains name/%s.",
lys_nodetype2str(snode->nodetype), snode->name, expected, lyjson_token2str(status));
LOG_LOCBACK(0, parent ? 1 : 0, 0, 0);
@@ -1312,14 +1321,14 @@
*
* @param[in] ctx libyang context
* @param[in] in Input structure.
- * @param[in] parse_options Options for parser, see @ref dataparseroptions.
- * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
+ * @param[in] parse_opts Options for parser, see @ref dataparseroptions.
+ * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions.
* @param[out] lydctx_p Data parser context to finish validation.
* @param[out] status Storage for the current context's status
* @return LY_ERR value.
*/
static LY_ERR
-lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_options, uint32_t validate_options,
+lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts,
struct lyd_json_ctx **lydctx_p, enum LYJSON_PARSER_STATUS *status)
{
LY_ERR ret = LY_SUCCESS;
@@ -1332,8 +1341,8 @@
/* init context */
lydctx = calloc(1, sizeof *lydctx);
LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
- lydctx->parse_options = parse_options;
- lydctx->validate_options = validate_options;
+ lydctx->parse_opts = parse_opts;
+ lydctx->val_opts = val_opts;
lydctx->free = lyd_json_ctx_free;
/* starting top-level */
@@ -1360,394 +1369,79 @@
}
LY_ERR
-lyd_parse_json_data(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_options, uint32_t validate_options,
- struct lyd_node **tree_p, struct lyd_ctx **lydctx_p)
+lyd_parse_json(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in,
+ uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, struct ly_set *parsed, struct lyd_ctx **lydctx_p)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR rc = LY_SUCCESS;
struct lyd_json_ctx *lydctx = NULL;
enum LYJSON_PARSER_STATUS status;
+ uint32_t int_opts;
- assert(tree_p);
- *tree_p = NULL;
-
- ret = lyd_parse_json_init(ctx, in, parse_options, validate_options, &lydctx, &status);
- LY_CHECK_GOTO(ret || status == LYJSON_END || status == LYJSON_OBJECT_EMPTY, cleanup);
+ rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx, &status);
+ LY_CHECK_GOTO(rc || status == LYJSON_END || status == LYJSON_OBJECT_EMPTY, cleanup);
assert(status == LYJSON_OBJECT);
+ switch (data_type) {
+ case LYD_TYPE_YANG_DATA:
+ int_opts = LYD_INTOPT_WITH_SIBLINGS;
+ break;
+ case LYD_TYPE_YANG_RPC:
+ int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_YANG_NOTIF:
+ int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_YANG_REPLY:
+ int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ default:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+ lydctx->int_opts = int_opts;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+
/* read subtree(s) */
- while (lydctx->jsonctx->in->current[0] && status != LYJSON_OBJECT_CLOSED) {
- ret = lydjson_subtree_r(lydctx, NULL, tree_p);
- LY_CHECK_GOTO(ret, cleanup);
+ while (lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) {
+ rc = lydjson_subtree_r(lydctx, parent, first_p, parsed);
+ LY_CHECK_GOTO(rc, cleanup);
status = lyjson_ctx_status(lydctx->jsonctx, 0);
+
+ if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
+ break;
+ }
+ }
+
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] &&
+ (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_OBJECT_CLOSED)) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ rc = LY_EVALID;
+ goto cleanup;
}
/* finish linking metadata */
- ret = lydjson_metadata_finish(lydctx, tree_p);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p);
+ LY_CHECK_GOTO(rc, cleanup);
cleanup:
/* there should be no unresolved types stored */
- assert(!(parse_options & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
+ assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
!lydctx->node_when.count));
- if (ret) {
+ if (rc) {
lyd_json_ctx_free((struct lyd_ctx *)lydctx);
- lyd_free_all(*tree_p);
- *tree_p = NULL;
} else {
*lydctx_p = (struct lyd_ctx *)lydctx;
}
-
- return ret;
-}
-
-#if 0
-/**
- * @brief Parse optional JSON envelope around the Notification data, including the eventTime data.
- *
- * @param[in] jsonctx JSON parser context
- * @param[out] envp_p Pointer to the created envelope opaq container.
- * @param[out] status Storage for the current context's status
- * @return LY_SUCCESS if the envelope present and successfully parsed.
- * @return LY_ENOT in case there is not the expected envelope.
- * @return LY_ERR in case of parsing failure.
- */
-static LY_ERR
-lydjson_notif_envelope(struct lyjson_ctx *jsonctx, struct lyd_node **envp_p, enum LYJSON_PARSER_STATUS *status_p)
-{
- LY_ERR ret = LY_ENOT, r;
- const char *name, *prefix, *value = NULL;
- size_t name_len, prefix_len, value_len;
- ly_bool is_attr, dynamic = 0;
- enum LYJSON_PARSER_STATUS status;
- struct lyd_node *et;
-
- *envp_p = NULL;
-
- /* backup the context */
- lyjson_ctx_backup(jsonctx);
-
- /* "notification" envelope */
- lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
- LY_CHECK_GOTO(is_attr, cleanup);
- LY_CHECK_GOTO(prefix_len != 13 || strncmp(prefix, "ietf-restconf", 13), cleanup);
- LY_CHECK_GOTO(name_len != 12 || strncmp(name, "notification", name_len), cleanup);
-
- r = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
- LY_CHECK_GOTO(status != LYJSON_OBJECT, cleanup);
-
- /* "eventTime" child */
- lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
- LY_CHECK_GOTO(prefix_len || is_attr, cleanup);
- LY_CHECK_GOTO(name_len != 9 || strncmp(name, "eventTime", name_len), cleanup);
-
- /* go for the data */
- r = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
- LY_CHECK_GOTO(status != LYJSON_STRING, cleanup);
-
- value = jsonctx->value;
- value_len = jsonctx->value_len;
- dynamic = jsonctx->dynamic;
- jsonctx->dynamic = 0;
-
- r = lyjson_ctx_next(jsonctx, &status);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
- LY_CHECK_GOTO(status != LYJSON_OBJECT, cleanup);
- /* now the notificationContent is expected, which will be parsed by the caller */
-
- /* create notification envelope */
- ret = lyd_create_opaq(jsonctx->ctx, "notification", ly_strlen_const("notification"), NULL, 0,
- "ietf-restconf", ly_strlen_const("ietf-restconf"), "", ly_strlen_const(""), NULL, LY_PREF_JSON, NULL,
- LYD_NODEHINT_ENVELOPE, envp_p);
- LY_CHECK_GOTO(ret, cleanup);
- /* create notification envelope */
- ret = lyd_create_opaq(jsonctx->ctx, "eventTime", ly_strlen_const("eventTime"), NULL, 0,
- "ietf-restconf", ly_strlen_const("ietf-restconf"), value, value_len, &dynamic, LY_PREF_JSON, NULL,
- LYD_VALHINT_STRING, &et);
- LY_CHECK_GOTO(ret, cleanup);
- /* insert eventTime into notification */
- lyd_insert_node(*envp_p, NULL, et);
-
- ret = LY_SUCCESS;
- *status_p = status;
-cleanup:
- if (ret) {
- /* restore the context */
- lyjson_ctx_restore(jsonctx);
- if (dynamic) {
- free((char *)value);
- }
- }
- return ret;
-}
-
-#endif
-
-LY_ERR
-lyd_parse_json_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p)
-{
- LY_ERR ret = LY_SUCCESS;
- struct lyd_json_ctx *lydctx = NULL;
- struct lyd_node *tree = NULL;
- enum LYJSON_PARSER_STATUS status;
-
- /* init */
- ret = lyd_parse_json_init(ctx, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &lydctx, &status);
- LY_CHECK_GOTO(ret || status == LYJSON_END || status == LYJSON_OBJECT_EMPTY, cleanup);
-
- lydctx->int_opts = LYD_INTOPT_NOTIF;
-
-#if 0
- /* parse "notification" and "eventTime", if present */
- ret = lydjson_notif_envelope(lydctx->jsonctx, &ntf_e, &status);
- if (ret == LY_ENOT) {
- ret = LY_SUCCESS;
- } else if (ret) {
- goto cleanup;
- }
-#endif
-
- assert(status == LYJSON_OBJECT);
-
- /* read subtree */
- ret = lydjson_subtree_r(lydctx, NULL, &tree);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* finish linking metadata */
- ret = lydjson_metadata_finish(lydctx, &tree);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* make sure we have parsed some notification */
- if (!lydctx->op_node) {
- LOGVAL(ctx, LYVE_DATA, "Missing the \"notification\" node.");
- ret = LY_EVALID;
- goto cleanup;
- } else if (lydctx->jsonctx->in->current[0] && (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_OBJECT_CLOSED)) {
- LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling element of \"%s\".",
- tree->schema->name);
- ret = LY_EVALID;
- goto cleanup;
- }
-
- if (ntf_p) {
- *ntf_p = lydctx->op_node;
- }
- assert(tree);
- if (tree_p) {
- *tree_p = tree;
- }
-
-cleanup:
- /* we have used parse_only flag */
- assert(!lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count && !lydctx->node_when.count));
-
- lyd_json_ctx_free((struct lyd_ctx *)lydctx);
- if (ret) {
- lyd_free_all(tree);
- }
- return ret;
-}
-
-#if 0
-/**
- * @brief Parse optional JSON envelope around the processed content.
- *
- * @param[in] jsonctx JSON parser context
- * @param[in] parent Parent node (some other envelope).
- * @param[in] module_key Name of the module where the envelope element is expected.
- * @param[in] object_id Name of the envelope object.
- * @param[out] envp_p Pointer to the created envelope opaq container.
- * @param[out] status Storage for the current context's status
- * @return LY_SUCCESS if the envelope present and successfully parsed.
- * @return LY_ENOT in case there is not the expected envelope.
- * @return LY_ERR in case of parsing failure.
- */
-static LY_ERR
-lydjson_object_envelope(struct lyjson_ctx *jsonctx, struct lyd_node *parent, const char *module_key,
- const char *object_id, struct lyd_node **envp_p, enum LYJSON_PARSER_STATUS *status)
-{
- LY_ERR ret = LY_ENOT, r;
- const char *name, *prefix;
- size_t name_len, prefix_len;
- ly_bool is_attr;
-
- *envp_p = NULL;
-
- /* "id" envelope */
- lydjson_parse_name(jsonctx->value, jsonctx->value_len, &name, &name_len, &prefix, &prefix_len, &is_attr);
- LY_CHECK_GOTO(is_attr, cleanup);
- LY_CHECK_GOTO(lydjson_get_node_prefix(parent, prefix, prefix_len, &prefix, &prefix_len), cleanup);
- LY_CHECK_GOTO(prefix_len != strlen(module_key) || strncmp(prefix, module_key, prefix_len), cleanup);
- LY_CHECK_GOTO(name_len != strlen(object_id) || strncmp(name, object_id, name_len), cleanup);
-
- r = lyjson_ctx_next(jsonctx, status);
- LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
- LY_CHECK_GOTO(*status != LYJSON_OBJECT, cleanup);
-
- /* create the object envelope */
- ret = lyd_create_opaq(jsonctx->ctx, object_id, strlen(object_id), NULL, 0, module_key, ly_strlen(module_key), "", 0,
- NULL, LY_PREF_JSON, NULL, LYD_NODEHINT_ENVELOPE, envp_p);
- LY_CHECK_GOTO(ret, cleanup);
-
- if (parent) {
- lyd_insert_node(parent, NULL, *envp_p);
- }
-
- ret = LY_SUCCESS;
-cleanup:
- return ret;
-}
-
-static LY_ERR
-lydjson_object_envelope_close(struct lyjson_ctx *jsonctx, const char *object_id, enum LYJSON_PARSER_STATUS *status)
-{
- LY_CHECK_RET(lyjson_ctx_next(jsonctx, status));
- if (*status == LYJSON_END) {
- LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
- return LY_EVALID;
- } else if (*status != LYJSON_OBJECT_CLOSED) {
- LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected sibling member \"%.*s\" of \"%s\".",
- jsonctx->value_len, jsonctx->value, object_id);
- return LY_EVALID;
- }
- return LY_SUCCESS;
-}
-
-#endif
-
-LY_ERR
-lyd_parse_json_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
-{
- LY_ERR ret = LY_SUCCESS;
- struct lyd_json_ctx *lydctx = NULL;
- struct lyd_node *tree = NULL;
- enum LYJSON_PARSER_STATUS status;
-
- /* init */
- ret = lyd_parse_json_init(ctx, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &lydctx, &status);
- LY_CHECK_GOTO(ret || status == LYJSON_END || status == LYJSON_OBJECT_EMPTY, cleanup);
-
- lydctx->int_opts = LYD_INTOPT_RPC;
-
-#if 0
- /* process envelope(s), if present */
-
- /* process rpc */
- ret = lydjson_object_envelope(lydctx->jsonctx, NULL, "ietf-netconf", "rpc", &rpc_e, &status);
- if (ret == LY_ENOT) {
- ret = LY_SUCCESS;
- goto parse_content;
- } else if (ret) {
- goto cleanup;
- }
- /* process action */
- ret = lydjson_object_envelope(lydctx->jsonctx, rpc_e, "yang", "action", &act_e, &status);
- if (ret == LY_ENOT) {
- ret = LY_SUCCESS;
- goto parse_content;
- } else if (ret) {
- goto cleanup;
- }
-
-parse_content:
-#endif
- assert(status == LYJSON_OBJECT);
-
- /* read subtree(s) */
- ret = lydjson_subtree_r(lydctx, NULL, &tree);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* finish linking metadata */
- ret = lydjson_metadata_finish(lydctx, &tree);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* make sure we have parsed some operation */
- if (!lydctx->op_node) {
- LOGVAL(ctx, LYVE_DATA, "Missing the rpc/action node.");
- ret = LY_EVALID;
- goto cleanup;
- } else if (lydctx->jsonctx->in->current[0] && (lyjson_ctx_status(lydctx->jsonctx, 0) != LYJSON_OBJECT_CLOSED)) {
- LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling element of \"%s\".",
- tree->schema->name);
- ret = LY_EVALID;
- goto cleanup;
- }
-
- if (op_p) {
- *op_p = lydctx->op_node;
- }
- assert(tree);
- if (tree_p) {
- *tree_p = tree;
- }
-
-cleanup:
- /* we have used parse_only flag */
- assert(!lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count && !lydctx->node_when.count));
-
- lyd_json_ctx_free((struct lyd_ctx *)lydctx);
- if (ret) {
- lyd_free_all(tree);
- }
- return ret;
-}
-
-LY_ERR
-lyd_parse_json_reply(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
-{
- LY_ERR ret = LY_SUCCESS;
- struct lyd_json_ctx *lydctx = NULL;
- struct lyd_node *tree = NULL;
- enum LYJSON_PARSER_STATUS status;
-
- /* init */
- ret = lyd_parse_json_init(ctx, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &lydctx, &status);
- LY_CHECK_GOTO(ret || status == LYJSON_END || status == LYJSON_OBJECT_EMPTY, cleanup);
-
- lydctx->int_opts = LYD_INTOPT_REPLY;
-
-#if 0
- /* parse "rpc-reply", if any */
- ret = lydjson_object_envelope(lydctx->jsonctx, NULL, "ietf-netconf", "rpc-reply", &rpcr_e, &status);
- if (ret == LY_ENOT) {
- ret = LY_SUCCESS;
- } else if (ret) {
- goto cleanup;
- }
-#endif
-
- assert(status == LYJSON_OBJECT);
-
- /* read subtree(s) */
- while (lydctx->jsonctx->in->current[0] && status != LYJSON_OBJECT_CLOSED) {
- ret = lydjson_subtree_r(lydctx, NULL, &tree);
- LY_CHECK_GOTO(ret, cleanup);
-
- status = lyjson_ctx_status(lydctx->jsonctx, 0);
- }
-
- /* finish linking metadata */
- ret = lydjson_metadata_finish(lydctx, &tree);
- LY_CHECK_GOTO(ret, cleanup);
-
- if (op_p) {
- *op_p = lydctx->op_node;
- }
- if (tree_p) {
- *tree_p = tree;
- }
-
-cleanup:
- /* we have used parse_only flag */
- assert(!lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count && !lydctx->node_when.count));
-
- lyd_json_ctx_free((struct lyd_ctx *)lydctx);
- if (ret) {
- lyd_free_all(tree);
- }
- return ret;
+ return rc;
}
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index f75e57c..e755160 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -358,7 +358,7 @@
LY_CHECK_GOTO(ret, cleanup);
/* find model */
- ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_options, &mod);
+ ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_opts, &mod);
LY_CHECK_GOTO(ret, cleanup);
if (!mod) {
@@ -650,7 +650,7 @@
}
}
- if (!sibling && (lybctx->parse_options & LYD_PARSE_STRICT)) {
+ if (!sibling && (lybctx->parse_opts & LYD_PARSE_STRICT)) {
if (mod) {
LOGVAL(lybctx->lybctx->ctx, LYVE_REFERENCE, "Failed to find matching hash for a top-level node"
" from \"%s\".", mod->name);
@@ -693,7 +693,7 @@
* @return LY_ERR value.
*/
static LY_ERR
-lyb_parse_subtree_r(struct lyd_lyb_ctx *lybctx, struct lyd_node_inner *parent, struct lyd_node **first)
+lyb_parse_subtree_r(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
{
LY_ERR ret = LY_SUCCESS;
struct lyd_node *node = NULL, *tree;
@@ -715,7 +715,7 @@
if (!parent) {
/* top-level, read module name */
- ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_options, &mod);
+ ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_opts, &mod);
LY_CHECK_GOTO(ret, cleanup);
/* read hash, find the schema node starting from mod */
@@ -727,7 +727,7 @@
LY_CHECK_GOTO(ret, cleanup);
}
- if (!snode && !(lybctx->parse_options & LYD_PARSE_OPAQ)) {
+ if (!snode && !(lybctx->parse_opts & LYD_PARSE_OPAQ)) {
/* unknown data, skip them */
lyb_skip_subtree(lybctx->lybctx);
goto stop_subtree;
@@ -777,7 +777,7 @@
/* process children */
while (LYB_LAST_SUBTREE(lybctx->lybctx).written) {
- ret = lyb_parse_subtree_r(lybctx, (struct lyd_node_inner *)node, NULL);
+ ret = lyb_parse_subtree_r(lybctx, node, NULL, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
} else if (snode->nodetype & LYD_NODE_TERM) {
@@ -802,19 +802,18 @@
/* process children */
while (LYB_LAST_SUBTREE(lybctx->lybctx).written) {
- ret = lyb_parse_subtree_r(lybctx, (struct lyd_node_inner *)node, NULL);
+ ret = lyb_parse_subtree_r(lybctx, node, NULL, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
- if (!(lybctx->parse_options & LYD_PARSE_ONLY)) {
+ if (!(lybctx->parse_opts & LYD_PARSE_ONLY)) {
/* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_children_p(node), snode, NULL, NULL);
+ ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, NULL);
LY_CHECK_GOTO(ret, cleanup);
/* add any missing default children */
- ret = lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, &lybctx->unres_node_type,
- &lybctx->when_check, (lybctx->validate_options & LYD_VALIDATE_NO_STATE) ?
- LYD_IMPLICIT_NO_STATE : 0, NULL);
+ ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lybctx->node_types,
+ &lybctx->node_when, (lybctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
LY_CHECK_GOTO(ret, cleanup);
}
@@ -903,9 +902,14 @@
}
/* insert, keep first pointer correct */
- lyd_insert_node(&parent->node, first, node);
- while (!parent && (*first)->prev->next) {
- *first = (*first)->prev;
+ lyd_insert_node(parent, first_p, node);
+ while (!parent && (*first_p)->prev->next) {
+ *first_p = (*first_p)->prev;
+ }
+
+ /* rememeber a successfully parsed node */
+ if (parsed) {
+ ly_set_add(parsed, node, 1, NULL);
}
node = NULL;
@@ -1013,130 +1017,98 @@
return LY_SUCCESS;
}
-/**
- * @param[in] ctx libyang context for logging
- * @param[in] parent Parent node where to connect the parsed data, required for reply where the reply data are connected
- * with the request operation
- * @param[in] in Input structure.
- * @param[in] parse_options Options for parser, see @ref dataparseroptions.
- * @param[in] validate_options Options for the validation phase, see @ref datavalidationoptions.
- * @param[in] data_type Internal data parser flag to distnguish type of the data to parse (RPC/Reply/Notification/regular data].
- * @param[out] tree_p Parsed data tree. Note that NULL can be a valid result.
- * @param[out] op_p Optional pointer to the actual operation. Useful for action and inner notifications.
- * @param[out] lydctx_p Data parser context to finish validation.
- */
-static LY_ERR
-lyd_parse_lyb_(const struct ly_ctx *ctx, struct lyd_node_inner **parent, struct ly_in *in, uint32_t parse_options,
- uint32_t validate_options, uint32_t data_type, struct lyd_node **tree_p, struct lyd_node **op_p,
- struct lyd_ctx **lydctx_p)
+LY_ERR
+lyd_parse_lyb(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in,
+ uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, struct ly_set *parsed, struct lyd_ctx **lydctx_p)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR rc = LY_SUCCESS;
struct lyd_lyb_ctx *lybctx;
- struct lyd_node *tree = NULL;
+ uint32_t int_opts;
- assert(!(parse_options & ~LYD_PARSE_OPTS_MASK));
- assert(!(validate_options & ~LYD_VALIDATE_OPTS_MASK));
+ assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
+ assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
lybctx = calloc(1, sizeof *lybctx);
LY_CHECK_ERR_RET(!lybctx, LOGMEM(ctx), LY_EMEM);
lybctx->lybctx = calloc(1, sizeof *lybctx->lybctx);
- LY_CHECK_ERR_GOTO(!lybctx->lybctx, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ LY_CHECK_ERR_GOTO(!lybctx->lybctx, LOGMEM(ctx); rc = LY_EMEM, cleanup);
lybctx->lybctx->in = in;
lybctx->lybctx->ctx = ctx;
- lybctx->parse_options = parse_options;
- lybctx->validate_options = validate_options;
- lybctx->int_opts = data_type;
+ lybctx->parse_opts = parse_opts;
+ lybctx->val_opts = val_opts;
lybctx->free = lyd_lyb_ctx_free;
+ switch (data_type) {
+ case LYD_TYPE_YANG_DATA:
+ int_opts = LYD_INTOPT_WITH_SIBLINGS;
+ break;
+ case LYD_TYPE_YANG_RPC:
+ int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_YANG_NOTIF:
+ int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_YANG_REPLY:
+ int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ default:
+ LOGINT(ctx);
+ rc = LY_EINT;
+ goto cleanup;
+ }
+ lybctx->int_opts = int_opts;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lybctx->op_node), cleanup);
+
/* read magic number */
- ret = lyb_parse_magic_number(lybctx->lybctx);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lyb_parse_magic_number(lybctx->lybctx);
+ LY_CHECK_GOTO(rc, cleanup);
/* read header */
- ret = lyb_parse_header(lybctx->lybctx);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lyb_parse_header(lybctx->lybctx);
+ LY_CHECK_GOTO(rc, cleanup);
/* read used models */
- ret = lyb_parse_data_models(lybctx->lybctx, lybctx->parse_options);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lyb_parse_data_models(lybctx->lybctx, lybctx->parse_opts);
+ LY_CHECK_GOTO(rc, cleanup);
/* read subtree(s) */
while (lybctx->lybctx->in->current[0]) {
- ret = lyb_parse_subtree_r(lybctx, parent ? *parent : NULL, &tree);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lyb_parse_subtree_r(lybctx, parent, first_p, parsed);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
+ break;
+ }
+ }
+
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lybctx->lybctx->in->current[0]) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lybctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ rc = LY_EVALID;
+ goto cleanup;
}
/* read the last zero, parsing finished */
ly_in_skip(lybctx->lybctx->in, 1);
- if (data_type == (LYD_INTOPT_RPC | LYD_INTOPT_REPLY)) {
- /* make sure we have parsed some operation */
- if (!lybctx->op_node) {
- LOGVAL(ctx, LYVE_DATA, "Missing the \"rpc\"/\"action\" node.");
- ret = LY_EVALID;
- goto cleanup;
- }
-
- if (op_p) {
- *op_p = lybctx->op_node;
- }
- assert(tree);
-
- } else if (data_type == LYD_INTOPT_NOTIF) {
- /* make sure we have parsed some notification */
- if (!lybctx->op_node) {
- LOGVAL(ctx, LYVE_DATA, "Missing the \"notification\" node.");
- ret = LY_EVALID;
- goto cleanup;
- }
-
- if (op_p) {
- *op_p = lybctx->op_node;
- }
- assert(tree);
- }
-
cleanup:
- if (ret) {
+ /* there should be no unres stored if validation should be skipped */
+ assert(!(parse_opts & LYD_PARSE_ONLY) || (!lybctx->node_types.count && !lybctx->meta_types.count &&
+ !lybctx->node_when.count));
+
+ if (rc) {
lyd_lyb_ctx_free((struct lyd_ctx *)lybctx);
- lyd_free_all(tree);
} else {
- if (lydctx_p) {
- *lydctx_p = (struct lyd_ctx *)lybctx;
- } else {
- lyd_lyb_ctx_free((struct lyd_ctx *)lybctx);
- }
- if (tree_p) {
- *tree_p = tree;
- }
+ *lydctx_p = (struct lyd_ctx *)lybctx;
}
- return ret;
-}
-
-LY_ERR
-lyd_parse_lyb_data(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_options, uint32_t validate_options,
- struct lyd_node **tree_p, struct lyd_ctx **lydctx_p)
-{
- return lyd_parse_lyb_(ctx, NULL, in, parse_options, validate_options, 0, tree_p, NULL, lydctx_p);
-}
-
-LY_ERR
-lyd_parse_lyb_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
-{
- return lyd_parse_lyb_(ctx, NULL, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LYD_INTOPT_RPC, tree_p, op_p, NULL);
-}
-
-LY_ERR
-lyd_parse_lyb_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p)
-{
- return lyd_parse_lyb_(ctx, NULL, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LYD_INTOPT_NOTIF, tree_p, ntf_p, NULL);
-}
-
-LY_ERR
-lyd_parse_lyb_reply(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
-{
- return lyd_parse_lyb_(ctx, NULL, in, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LYD_INTOPT_REPLY, tree_p, op_p, NULL);
+ return rc;
}
API int
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 2c96b6d..4228dea 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -36,8 +36,8 @@
* Note that the structure maps to the lyd_ctx which is common for all the data parsers
*/
struct lyd_xml_ctx {
- uint32_t parse_options; /**< various @ref dataparseroptions. */
- uint32_t validate_options; /**< various @ref datavalidationoptions. */
+ uint32_t parse_opts; /**< various @ref dataparseroptions. */
+ uint32_t val_opts; /**< various @ref datavalidationoptions. */
uint32_t int_opts; /**< internal data parser options */
uint32_t path_len; /**< used bytes in the path buffer */
char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */
@@ -78,7 +78,7 @@
if (!xmlctx->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 (lydctx->parse_options & LYD_PARSE_STRICT) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Missing mandatory prefix for XML metadata \"%.*s\".",
xmlctx->name_len, xmlctx->name);
goto cleanup;
@@ -101,7 +101,7 @@
mod = ly_ctx_get_module_implemented_ns(xmlctx->ctx, ns->uri);
if (!mod) {
/* module is not implemented or not present in the schema */
- if (lydctx->parse_options & LYD_PARSE_STRICT) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(xmlctx->ctx, LYVE_REFERENCE,
"Unknown (or not implemented) YANG module with namespace \"%s\" for metadata \"%.*s%s%.*s\".",
ns->uri, xmlctx->prefix_len, xmlctx->prefix, xmlctx->prefix_len ? ":" : "", xmlctx->name_len,
@@ -308,7 +308,7 @@
size_t pprefix_len, pname_len;
struct lyxml_ctx *xmlctx = lydctx->xmlctx;
- if ((lydctx->parse_options & LYD_PARSE_OPAQ) && ((*snode)->nodetype & (LYD_NODE_TERM | LYS_LIST))) {
+ if ((lydctx->parse_opts & LYD_PARSE_OPAQ) && ((*snode)->nodetype & (LYD_NODE_TERM | LYS_LIST))) {
/* backup parser */
prev_status = xmlctx->status;
pprefix = xmlctx->prefix;
@@ -362,12 +362,14 @@
* @brief Parse XML subtree.
*
* @param[in] lydctx XML YANG data parser context.
- * @param[in] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
- * @param[out] node Resulting list of the parsed nodes.
+ * @param[in,out] parent Parent node where the children are inserted. NULL in case of parsing top-level elements.
+ * @param[in,out] first_p Pointer to the first (@p parent or top-level) child. In case there were already some siblings,
+ * this may point to a previously existing node.
+ * @param[in,out] parsed Optional set to add all the parsed siblings into.
* @return LY_ERR value.
*/
static LY_ERR
-lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node_inner *parent, struct lyd_node **first_p)
+lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed)
{
LY_ERR ret = LY_SUCCESS;
const char *prefix, *name;
@@ -385,6 +387,8 @@
LY_PREFIX_FORMAT format;
uint32_t getnext_opts;
+ assert(parent || first_p);
+
xmlctx = lydctx->xmlctx;
ctx = xmlctx->ctx;
getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
@@ -406,12 +410,12 @@
}
mod = ly_ctx_get_module_implemented_ns(ctx, ns->uri);
if (!mod) {
- if (lydctx->parse_options & LYD_PARSE_STRICT) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(ctx, LYVE_REFERENCE, "No module with namespace \"%s\" in the context.", ns->uri);
ret = LY_EVALID;
goto error;
}
- if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
+ if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
/* skip element with children */
LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), error);
return LY_SUCCESS;
@@ -426,11 +430,17 @@
if (mod && (!parent || parent->schema)) {
snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
if (!snode) {
- if (lydctx->parse_options & LYD_PARSE_STRICT) {
- LOGVAL(ctx, LYVE_REFERENCE, "Element \"%.*s\" not found in the \"%s\" module.", name_len, name, mod->name);
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+ if (parent) {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.", name_len, name,
+ parent->schema->name);
+ } else {
+ LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.", name_len, name,
+ mod->name);
+ }
ret = LY_EVALID;
goto error;
- } else if (!(lydctx->parse_options & LYD_PARSE_OPAQ)) {
+ } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
/* skip element with children */
LY_CHECK_GOTO(ret = lydxml_data_skip(xmlctx), error);
return LY_SUCCESS;
@@ -448,7 +458,7 @@
ret = lydxml_metadata(lydctx, &meta);
LY_CHECK_GOTO(ret, error);
} else {
- assert(lydctx->parse_options & LYD_PARSE_OPAQ);
+ assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
ret = lydxml_attrs(xmlctx, &attr);
LY_CHECK_GOTO(ret, error);
}
@@ -456,7 +466,7 @@
assert(xmlctx->status == LYXML_ELEM_CONTENT);
if (!snode) {
- assert(lydctx->parse_options & LYD_PARSE_OPAQ);
+ assert(lydctx->parse_opts & LYD_PARSE_OPAQ);
if (xmlctx->ws_only) {
/* ignore WS-only value */
@@ -480,7 +490,7 @@
/* process children */
while (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(lydctx, (struct lyd_node_inner *)node, lyd_node_children_p(node));
+ ret = lydxml_subtree_r(lydctx, node, lyd_node_child_p(node), NULL);
LY_CHECK_GOTO(ret, error);
}
} else if (snode->nodetype & LYD_NODE_TERM) {
@@ -491,9 +501,9 @@
if (parent && (node->schema->flags & LYS_KEY)) {
/* check the key order, the anchor must never be a key */
- anchor = lyd_insert_get_next_anchor(parent->child, node);
+ anchor = lyd_insert_get_next_anchor(lyd_child(parent), node);
if (anchor && (anchor->schema->flags & LYS_KEY)) {
- if (lydctx->parse_options & LYD_PARSE_STRICT) {
+ if (lydctx->parse_opts & LYD_PARSE_STRICT) {
LOGVAL(ctx, LYVE_DATA, "Invalid position of the key \"%s\" in a list.", node->schema->name);
ret = LY_EVALID;
goto error;
@@ -533,7 +543,7 @@
/* process children */
while (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(lydctx, (struct lyd_node_inner *)node, lyd_node_children_p(node));
+ ret = lydxml_subtree_r(lydctx, node, lyd_node_child_p(node), NULL);
LY_CHECK_GOTO(ret, error);
}
@@ -542,14 +552,14 @@
LY_CHECK_GOTO(ret = lyd_parse_check_keys(node), error);
}
- if (!(lydctx->parse_options & LYD_PARSE_ONLY)) {
+ if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {
/* new node validation, autodelete CANNOT occur, all nodes are new */
- ret = lyd_validate_new(lyd_node_children_p(node), snode, NULL, NULL);
+ ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, NULL);
LY_CHECK_GOTO(ret, error);
/* add any missing default children */
- ret = lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, &lydctx->node_types, &lydctx->node_when,
- (lydctx->validate_options & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
+ ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lydctx->node_types, &lydctx->node_when,
+ (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
LY_CHECK_GOTO(ret, error);
}
@@ -570,15 +580,15 @@
LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), error);
/* parse any data tree with correct options */
- prev_opts = lydctx->parse_options;
- lydctx->parse_options &= ~LYD_PARSE_STRICT;
- lydctx->parse_options |= LYD_PARSE_OPAQ;
+ prev_opts = lydctx->parse_opts;
+ lydctx->parse_opts &= ~LYD_PARSE_STRICT;
+ lydctx->parse_opts |= LYD_PARSE_OPAQ;
anchor = NULL;
while (xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(lydctx, NULL, &anchor);
- LY_CHECK_ERR_GOTO(ret, lydctx->parse_options = prev_opts, error);
+ ret = lydxml_subtree_r(lydctx, NULL, &anchor, NULL);
+ LY_CHECK_ERR_GOTO(ret, lydctx->parse_opts = prev_opts, error);
}
- lydctx->parse_options = prev_opts;
+ lydctx->parse_opts = prev_opts;
/* create node */
ret = lyd_create_any(snode, anchor, LYD_ANYDATA_DATATREE, 1, &node);
@@ -588,7 +598,7 @@
/* add/correct flags */
if (snode) {
- lyd_parse_set_data_flags(node, &lydctx->node_when, &meta, lydctx->parse_options);
+ lyd_parse_set_data_flags(node, &lydctx->node_when, &meta, lydctx->parse_opts);
}
/* parser next */
@@ -603,11 +613,16 @@
}
/* insert, keep first pointer correct */
- lyd_insert_node(&parent->node, first_p, node);
+ lyd_insert_node(parent, first_p, node);
while (!parent && (*first_p)->prev->next) {
*first_p = (*first_p)->prev;
}
+ /* rememeber a successfully parsed node */
+ if (parsed) {
+ ly_set_add(parsed, node, 1, NULL);
+ }
+
LOG_LOCBACK(node ? 1 : 0, node ? 1 : 0, 0, 0);
return LY_SUCCESS;
@@ -619,193 +634,31 @@
return ret;
}
-LY_ERR
-lyd_parse_xml_data(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_options, uint32_t validate_options,
- struct lyd_node **tree_p, struct lyd_ctx **lydctx_p)
-{
- LY_ERR ret = LY_SUCCESS;
- struct lyd_xml_ctx *lydctx;
-
- assert(!(parse_options & ~LYD_PARSE_OPTS_MASK));
- assert(!(validate_options & ~LYD_VALIDATE_OPTS_MASK));
-
- /* init context */
- lydctx = calloc(1, sizeof *lydctx);
- LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
- LY_CHECK_GOTO(ret = lyxml_ctx_new(ctx, in, &lydctx->xmlctx), cleanup);
- lydctx->parse_options = parse_options;
- lydctx->validate_options = validate_options;
- lydctx->free = lyd_xml_ctx_free;
-
- /* parse XML data */
- while (lydctx->xmlctx->status == LYXML_ELEMENT) {
- LY_CHECK_GOTO(ret = lydxml_subtree_r(lydctx, NULL, tree_p), cleanup);
- }
-
-cleanup:
- /* there should be no unres stored if validation should be skipped */
- assert(!(parse_options & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
- !lydctx->node_when.count));
-
- if (ret) {
- lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
- lyd_free_all(*tree_p);
- *tree_p = NULL;
- } else {
- *lydctx_p = (struct lyd_ctx *)lydctx;
-
- /* the XML context is no more needed, freeing it also stops logging line numbers which would be confusing now */
- lyxml_ctx_free(lydctx->xmlctx);
- lydctx->xmlctx = NULL;
- }
- return ret;
-}
-
-#if 0
+/**
+ * @brief Parse a specific XML element into an opaque node.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[in] name Name of the element.
+ * @param[in] uri URI of the element.
+ * @param[in] value Whether a value is expected in the element.
+ * @param[out] evnp Parsed envelope (opaque node).
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the specified element did not match.
+ * @return LY_ERR value on error.
+ */
static LY_ERR
-lydxml_envelope(struct lyxml_ctx *xmlctx, const char *name, const char *uri, struct lyd_node **envp)
+lydxml_envelope(struct lyxml_ctx *xmlctx, const char *name, const char *uri, ly_bool value, struct lyd_node **envp)
{
- LY_ERR ret = LY_SUCCESS;
- const struct lyxml_ns *ns = NULL;
+ LY_ERR rc = LY_SUCCESS;
+ const struct lyxml_ns *ns;
struct lyd_attr *attr = NULL;
const char *prefix;
size_t prefix_len;
- *envp = NULL;
-
assert(xmlctx->status == LYXML_ELEMENT);
if (ly_strncmp(name, xmlctx->name, xmlctx->name_len)) {
/* not the expected element */
- return LY_SUCCESS;
- }
-
- prefix = xmlctx->prefix;
- prefix_len = xmlctx->prefix_len;
- ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
- if (!ns) {
- LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".",
- prefix_len, prefix);
- return LY_EVALID;
- } else if (strcmp(ns->uri, uri)) {
- /* different namespace */
- return LY_SUCCESS;
- }
-
- LY_CHECK_RET(lyxml_ctx_next(xmlctx));
-
- /* create attributes */
- if (xmlctx->status == LYXML_ATTRIBUTE) {
- LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
- }
-
- if (!xmlctx->ws_only) {
- LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected value \"%.*s\" in the \"%s\" element.",
- xmlctx->value_len, xmlctx->value, name);
- ret = LY_EVALID;
- goto cleanup;
- }
-
- /* parser next element */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
-
- /* create node */
- ret = lyd_create_opaq(xmlctx->ctx, name, strlen(name), prefix, prefix_len, uri, strlen(uri), "", 0, NULL,
- LY_PREF_XML, NULL, LYD_NODEHINT_ENVELOPE, envp);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* assign atributes */
- ((struct lyd_node_opaq *)(*envp))->attr = attr;
- attr = NULL;
-
-cleanup:
- lyd_free_attr_siblings(xmlctx->ctx, attr);
- return ret;
-}
-
-#endif
-
-LY_ERR
-lyd_parse_xml_rpc(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
-{
- LY_ERR ret = LY_SUCCESS;
- struct lyd_xml_ctx lydctx = {0};
- struct lyd_node *tree = NULL;
-
- /* init */
- LY_CHECK_GOTO(ret = lyxml_ctx_new(ctx, in, &lydctx.xmlctx), cleanup);
- lydctx.parse_options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
- lydctx.int_opts = LYD_INTOPT_RPC;
-
-#if 0
- /* parse "rpc", if any */
- LY_CHECK_GOTO(ret = lydxml_envelope(lydctx.xmlctx, "rpc", "urn:ietf:params:xml:ns:netconf:base:1.0", &rpc_e), cleanup);
-
- if (rpc_e) {
- /* parse "action", if any */
- LY_CHECK_GOTO(ret = lydxml_envelope(lydctx.xmlctx, "action", "urn:ietf:params:xml:ns:yang:1", &act_e), cleanup);
- }
-#endif
-
- /* parse the rest of data normally */
- LY_CHECK_GOTO(ret = lydxml_subtree_r(&lydctx, NULL, &tree), cleanup);
-
- /* make sure we have parsed some operation and it is the only subtree */
- if (!lydctx.op_node) {
- LOGVAL(ctx, LYVE_DATA, "Missing the \"rpc\"/\"action\" node.");
- ret = LY_EVALID;
- goto cleanup;
- } else if (lydctx.xmlctx->status == LYXML_ELEMENT) {
- LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling element of \"%s\".",
- tree->schema->name);
- ret = LY_EVALID;
- goto cleanup;
- }
-
- if (op_p) {
- *op_p = lydctx.op_node;
- }
- assert(tree);
- if (tree_p) {
- *tree_p = tree;
- }
-
-cleanup:
- /* we have used parse_only flag */
- assert(!lydctx.node_types.count && !lydctx.meta_types.count && !lydctx.node_when.count);
- lyxml_ctx_free(lydctx.xmlctx);
- if (ret) {
- lyd_free_all(tree);
- }
- return ret;
-}
-
-#if 0
-static LY_ERR
-lydxml_notif_envelope(struct lyxml_ctx *xmlctx, struct lyd_node **envp)
-{
- LY_ERR ret = LY_SUCCESS;
- const struct lyxml_ns *ns = NULL;
- struct lyd_attr *attr = NULL;
- struct lyd_node *et;
- const char *prefix;
- size_t prefix_len;
-
- *envp = NULL;
-
- /* container envelope */
- LY_CHECK_GOTO(ret = lydxml_envelope(xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0",
- envp), cleanup);
-
- /* no envelope, fine */
- if (!*envp) {
- goto cleanup;
- }
-
- /* child "eventTime" */
- if ((xmlctx->status != LYXML_ELEMENT) || ly_strncmp("eventTime", xmlctx->name, xmlctx->name_len)) {
- LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Missing the \"eventTime\" element.");
- ret = LY_EVALID;
- goto cleanup;
+ return LY_ENOT;
}
prefix = xmlctx->prefix;
@@ -813,12 +666,10 @@
ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
if (!ns) {
LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", prefix_len, prefix);
- ret = LY_EVALID;
- goto cleanup;
- } else if (strcmp(ns->uri, "urn:ietf:params:xml:ns:netconf:notification:1.0")) {
- LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid namespace \"%s\" of \"eventTime\".", ns->uri);
- ret = LY_EVALID;
- goto cleanup;
+ return LY_EVALID;
+ } else if (strcmp(ns->uri, uri)) {
+ /* different namespace */
+ return LY_ENOT;
}
LY_CHECK_RET(lyxml_ctx_next(xmlctx));
@@ -828,135 +679,761 @@
LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
}
- /* validate value */
- /* TODO */
- /*if (!xmlctx->ws_only) {
- LOGVAL(xmlctx->ctx, LY_VLOG_LINE, &xmlctx->line, LYVE_SYNTAX, "Unexpected value \"%.*s\" in the \"%s\" element.",
- xmlctx->value_len, xmlctx->value, name);
- ret = LY_EVALID;
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ if (!value && !xmlctx->ws_only) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected value \"%.*s\" in the \"%s\" element.",
+ xmlctx->value_len, xmlctx->value, name);
+ rc = LY_EVALID;
goto cleanup;
- }*/
+ }
/* create node */
- ret = lyd_create_opaq(xmlctx->ctx, "eventTime", ly_strlen_const("eventTime"), prefix, prefix_len,
- ns->uri, strlen(ns->uri), xmlctx->value, xmlctx->value_len, NULL, LY_PREF_XML, NULL, LYD_NODEHINT_ENVELOPE, &et);
- LY_CHECK_GOTO(ret, cleanup);
+ rc = lyd_create_opaq(xmlctx->ctx, name, strlen(name), prefix, prefix_len, uri, strlen(uri), xmlctx->value,
+ xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_PREF_XML, NULL, 0, envp);
+ LY_CHECK_GOTO(rc, cleanup);
/* assign atributes */
- ((struct lyd_node_opaq *)et)->attr = attr;
+ ((struct lyd_node_opaq *)(*envp))->attr = attr;
attr = NULL;
- /* insert */
- lyd_insert_node(*envp, NULL, et);
+ /* parser next element */
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
- /* finish parsing */
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+cleanup:
+ lyd_free_attr_siblings(xmlctx->ctx, attr);
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse all expected non-data XML elements of a NETCONF rpc message.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[out] evnp Parsed envelope(s) (opaque node).
+ * @param[out] int_opts Internal options for parsing the rest of YANG data.
+ * @param[out] close_elem Number of parsed opened elements that need to be closed.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydxml_env_netconf_rpc(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_node *child;
+
+ assert(envp && !*envp);
+
+ /* parse "rpc" */
+ r = lydxml_envelope(xmlctx, "rpc", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
+ if (r == LY_ENOT) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unexpected element \"%.*s\" instead of \"rpc\".", xmlctx->name_len,
+ xmlctx->name);
+ r = LY_EVALID;
+ }
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* parse "action", if any */
+ r = lydxml_envelope(xmlctx, "action", "urn:ietf:params:xml:ns:yang:1", 0, &child);
+ if (r == LY_SUCCESS) {
+ /* insert */
+ lyd_insert_node(*envp, NULL, child);
+
+ /* NETCONF action */
+ *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_ACTION;
+ *close_elem = 2;
+ } else if (r == LY_ENOT) {
+ /* NETCONF RPC */
+ *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_RPC;
+ *close_elem = 1;
+ } else {
+ rc = r;
+ goto cleanup;
+ }
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse all expected non-data XML elements of a NETCONF notification message.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[out] evnp Parsed envelope(s) (opaque node).
+ * @param[out] int_opts Internal options for parsing the rest of YANG data.
+ * @param[out] close_elem Number of parsed opened elements that need to be closed.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydxml_env_netconf_notif(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_node *child;
+
+ assert(envp && !*envp);
+
+ /* parse "notification" */
+ r = lydxml_envelope(xmlctx, "notification", "urn:ietf:params:xml:ns:netconf:notification:1.0", 0, envp);
+ if (r == LY_ENOT) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unexpected element \"%.*s\" instead of \"notification\".", xmlctx->name_len,
+ xmlctx->name);
+ r = LY_EVALID;
+ }
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* parse "eventTime" */
+ r = lydxml_envelope(xmlctx, "eventTime", "urn:ietf:params:xml:ns:netconf:notification:1.0", 1, &child);
+ if (r == LY_ENOT) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unexpected element \"%.*s\" instead of \"eventTime\".", xmlctx->name_len,
+ xmlctx->name);
+ r = LY_EVALID;
+ }
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* insert */
+ lyd_insert_node(*envp, NULL, child);
+
+ /* validate value */
+ /* TODO validate child->value as yang:date-and-time */
+
+ /* finish child parsing */
if (xmlctx->status != LYXML_ELEM_CLOSE) {
assert(xmlctx->status == LYXML_ELEMENT);
- LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected sibling element \"%.*s\" of \"eventTime\".",
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"eventTime\".",
xmlctx->name_len, xmlctx->name);
- ret = LY_EVALID;
+ rc = LY_EVALID;
goto cleanup;
}
- LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
+
+ /* NETCONF notification */
+ *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_NOTIF;
+ *close_elem = 1;
cleanup:
- if (ret) {
+ if (rc) {
lyd_free_tree(*envp);
- lyd_free_attr_siblings(xmlctx->ctx, attr);
+ *envp = NULL;
}
- return ret;
+ return rc;
}
-#endif
-
-LY_ERR
-lyd_parse_xml_notif(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **ntf_p)
+/**
+ * @brief Parse an XML element as an opaque node subtree.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[in] parent Parent to append nodes to.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_opaq_r(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
{
- LY_ERR ret = LY_SUCCESS;
- struct lyd_xml_ctx lydctx = {0};
- struct lyd_node *tree = NULL;
+ LY_ERR rc = LY_SUCCESS;
+ const struct lyxml_ns *ns;
+ struct lyd_attr *attr = NULL;
+ struct lyd_node *child = NULL;
+ const char *name, *prefix;
+ size_t name_len, prefix_len;
- /* init */
- LY_CHECK_GOTO(ret = lyxml_ctx_new(ctx, in, &lydctx.xmlctx), cleanup);
- lydctx.parse_options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
- lydctx.int_opts = LYD_INTOPT_NOTIF;
+ assert(xmlctx->status == LYXML_ELEMENT);
-#if 0
- /* parse "notification" and "eventTime", if present */
- LY_CHECK_GOTO(ret = lydxml_notif_envelope(lydctx.xmlctx, &ntf_e), cleanup);
-#endif
+ name = xmlctx->name;
+ name_len = xmlctx->name_len;
+ prefix = xmlctx->prefix;
+ prefix_len = xmlctx->prefix_len;
+ ns = lyxml_ns_get(&xmlctx->ns, prefix, prefix_len);
+ if (!ns) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", prefix_len, prefix);
+ return LY_EVALID;
+ }
- /* parse the rest of data normally */
- LY_CHECK_GOTO(ret = lydxml_subtree_r(&lydctx, NULL, &tree), cleanup);
+ LY_CHECK_RET(lyxml_ctx_next(xmlctx));
- /* make sure we have parsed some notification */
- if (!lydctx.op_node) {
- LOGVAL(ctx, LYVE_DATA, "Missing the \"notification\" node.");
- ret = LY_EVALID;
- goto cleanup;
- } else if (lydctx.xmlctx->status == LYXML_ELEMENT) {
- LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling element of \"%s\".",
- tree->schema->name);
- ret = LY_EVALID;
+ /* create attributes */
+ if (xmlctx->status == LYXML_ATTRIBUTE) {
+ LY_CHECK_RET(lydxml_attrs(xmlctx, &attr));
+ }
+
+ /* create node */
+ assert(xmlctx->status == LYXML_ELEM_CONTENT);
+ rc = lyd_create_opaq(xmlctx->ctx, name, name_len, prefix, prefix_len, ns->uri, strlen(ns->uri), xmlctx->value,
+ xmlctx->ws_only ? 0 : xmlctx->value_len, NULL, LY_PREF_XML, NULL, 0, &child);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ /* assign atributes */
+ ((struct lyd_node_opaq *)child)->attr = attr;
+ attr = NULL;
+
+ /* parser next element */
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
+
+ /* parse all the descendants */
+ while (xmlctx->status == LYXML_ELEMENT) {
+ rc = lydxml_opaq_r(xmlctx, child);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+
+ /* insert */
+ lyd_insert_node(parent, NULL, child);
+
+cleanup:
+ lyd_free_attr_siblings(xmlctx->ctx, attr);
+ if (rc) {
+ lyd_free_tree(child);
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse all expected non-data XML elements of the error-info element in NETCONF rpc-reply message.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[in] parent Parent to append nodes to.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_env_netconf_rpc_reply_error_info(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
+{
+ LY_ERR r;
+ struct lyd_node *child, *iter;
+ const struct lyxml_ns *ns;
+ ly_bool no_dup;
+
+ /* there must be some child */
+ if (xmlctx->status == LYXML_ELEM_CLOSE) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Missing child elements of \"error-info\".");
+ return LY_EVALID;
+ }
+
+ while (xmlctx->status == LYXML_ELEMENT) {
+ child = NULL;
+
+ /*
+ * session-id
+ */
+ r = lydxml_envelope(xmlctx, "session-id", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ /*
+ * bad-attribute
+ */
+ r = lydxml_envelope(xmlctx, "bad-attribute", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ /*
+ * bad-element
+ */
+ r = lydxml_envelope(xmlctx, "bad-element", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ /*
+ * bad-namespace
+ */
+ r = lydxml_envelope(xmlctx, "bad-namespace", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ if (r == LY_ENOT) {
+ assert(xmlctx->status == LYXML_ELEMENT);
+
+ /* learn namespace */
+ ns = lyxml_ns_get(&xmlctx->ns, xmlctx->prefix, xmlctx->prefix_len);
+ if (!ns) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", xmlctx->prefix_len, xmlctx->prefix);
+ r = LY_EVALID;
+ goto error;
+ } else if (!strcmp(ns->uri, "urn:ietf:params:xml:ns:netconf:base:1.0")) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"error-info\".",
+ xmlctx->name_len, xmlctx->name);
+ r = LY_EVALID;
+ goto error;
+ }
+
+ /* custom elements */
+ r = lydxml_opaq_r(xmlctx, parent);
+ LY_CHECK_GOTO(r, error);
+
+ no_dup = 0;
+ }
+
+check_child:
+ /* check for duplicates */
+ if (no_dup) {
+ LY_LIST_FOR(lyd_child(parent), iter) {
+ if ((((struct lyd_node_opaq *)iter)->name.name == ((struct lyd_node_opaq *)child)->name.name) &&
+ (((struct lyd_node_opaq *)iter)->name.module_ns == ((struct lyd_node_opaq *)child)->name.module_ns)) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Duplicate element \"%s\" in \"error-info\".",
+ ((struct lyd_node_opaq *)child)->name.name);
+ r = LY_EVALID;
+ goto error;
+ }
+ }
+ }
+
+ /* finish child parsing */
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"error-info\".",
+ xmlctx->name_len, xmlctx->name);
+ r = LY_EVALID;
+ goto error;
+ }
+ LY_CHECK_GOTO(r = lyxml_ctx_next(xmlctx), error);
+
+ /* insert */
+ lyd_insert_node(parent, NULL, child);
+ }
+
+ return LY_SUCCESS;
+
+error:
+ lyd_free_tree(child);
+ return r;
+}
+
+/**
+ * @brief Parse all expected non-data XML elements of the rpc-error element in NETCONF rpc-reply message.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[in] parent Parent to append nodes to.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lydxml_env_netconf_rpc_reply_error(struct lyxml_ctx *xmlctx, struct lyd_node *parent)
+{
+ LY_ERR r;
+ struct lyd_node *child, *iter;
+ const char *val;
+ ly_bool no_dup;
+
+ /* there must be some child */
+ if (xmlctx->status == LYXML_ELEM_CLOSE) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Missing child elements of \"rpc-error\".");
+ return LY_EVALID;
+ }
+
+ while (xmlctx->status == LYXML_ELEMENT) {
+ child = NULL;
+
+ /*
+ * error-type
+ */
+ r = lydxml_envelope(xmlctx, "error-type", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ val = ((struct lyd_node_opaq *)child)->value;
+ if (strcmp(val, "transport") && strcmp(val, "rpc") && strcmp(val, "protocol") && strcmp(val, "application")) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid value \"%s\" of element \"%s\".", val,
+ ((struct lyd_node_opaq *)child)->name.name);
+ r = LY_EVALID;
+ goto error;
+ }
+
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ /*
+ * error-tag
+ */
+ r = lydxml_envelope(xmlctx, "error-tag", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ val = ((struct lyd_node_opaq *)child)->value;
+ if (strcmp(val, "in-use") && strcmp(val, "invalid-value") && strcmp(val, "too-big") &&
+ strcmp(val, "missing-attribute") && strcmp(val, "bad-attribute") &&
+ strcmp(val, "unknown-attribute") && strcmp(val, "missing-element") && strcmp(val, "bad-element") &&
+ strcmp(val, "unknown-element") && strcmp(val, "unknown-namespace") && strcmp(val, "access-denied") &&
+ strcmp(val, "lock-denied") && strcmp(val, "resource-denied") && strcmp(val, "rollback-failed") &&
+ strcmp(val, "data-exists") && strcmp(val, "data-missing") && strcmp(val, "operation-not-supported") &&
+ strcmp(val, "operation-failed") && strcmp(val, "malformed-message")) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid value \"%s\" of element \"%s\".", val,
+ ((struct lyd_node_opaq *)child)->name.name);
+ r = LY_EVALID;
+ goto error;
+ }
+
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ /*
+ * error-severity
+ */
+ r = lydxml_envelope(xmlctx, "error-severity", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ val = ((struct lyd_node_opaq *)child)->value;
+ if (strcmp(val, "error") && strcmp(val, "warning")) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Invalid value \"%s\" of element \"%s\".", val,
+ ((struct lyd_node_opaq *)child)->name.name);
+ r = LY_EVALID;
+ goto error;
+ }
+
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ /*
+ * error-app-tag
+ */
+ r = lydxml_envelope(xmlctx, "error-app-tag", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ /*
+ * error-path
+ */
+ r = lydxml_envelope(xmlctx, "error-path", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ /*
+ * error-message
+ */
+ r = lydxml_envelope(xmlctx, "error-message", "urn:ietf:params:xml:ns:netconf:base:1.0", 1, &child);
+ if (r == LY_SUCCESS) {
+ no_dup = 1;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ /* error-info */
+ r = lydxml_envelope(xmlctx, "error-info", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, &child);
+ if (r == LY_SUCCESS) {
+ /* parse all the descendants */
+ LY_CHECK_GOTO(r = lydxml_env_netconf_rpc_reply_error_info(xmlctx, child), error);
+
+ no_dup = 0;
+ goto check_child;
+ } else if (r != LY_ENOT) {
+ goto error;
+ }
+
+ if (r == LY_ENOT) {
+ assert(xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"rpc-error\".",
+ xmlctx->name_len, xmlctx->name);
+ r = LY_EVALID;
+ goto error;
+ }
+
+check_child:
+ /* check for duplicates */
+ if (no_dup) {
+ LY_LIST_FOR(lyd_child(parent), iter) {
+ if ((((struct lyd_node_opaq *)iter)->name.name == ((struct lyd_node_opaq *)child)->name.name) &&
+ (((struct lyd_node_opaq *)iter)->name.module_ns == ((struct lyd_node_opaq *)child)->name.module_ns)) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Duplicate element \"%s\" in \"rpc-error\".",
+ ((struct lyd_node_opaq *)child)->name.name);
+ r = LY_EVALID;
+ goto error;
+ }
+ }
+ }
+
+ /* finish child parsing */
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"rpc-error\".",
+ xmlctx->name_len, xmlctx->name);
+ r = LY_EVALID;
+ goto error;
+ }
+ LY_CHECK_GOTO(r = lyxml_ctx_next(xmlctx), error);
+
+ /* insert */
+ lyd_insert_node(parent, NULL, child);
+ }
+
+ return LY_SUCCESS;
+
+error:
+ lyd_free_tree(child);
+ return r;
+}
+
+/**
+ * @brief Parse all expected non-data XML elements of a NETCONF rpc-reply message.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[out] evnp Parsed envelope(s) (opaque node).
+ * @param[out] int_opts Internal options for parsing the rest of YANG data.
+ * @param[out] close_elem Number of parsed opened elements that need to be closed.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydxml_env_netconf_reply(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts, uint32_t *close_elem)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ struct lyd_node *child = NULL;
+ const char *parsed_elem = NULL;
+
+ assert(envp && !*envp);
+
+ /* parse "rpc-reply" */
+ r = lydxml_envelope(xmlctx, "rpc-reply", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, envp);
+ if (r == LY_ENOT) {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unexpected element \"%.*s\" instead of \"rpc-reply\".", xmlctx->name_len,
+ xmlctx->name);
+ r = LY_EVALID;
+ }
+ LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
+
+ /* there must be some child */
+ if (xmlctx->status == LYXML_ELEM_CLOSE) {
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Missing child elements of \"rpc-reply\".");
+ rc = LY_EVALID;
goto cleanup;
}
- if (ntf_p) {
- *ntf_p = lydctx.op_node;
- }
- assert(tree);
- if (tree_p) {
- *tree_p = tree;
+ /* try to parse "ok" */
+ r = lydxml_envelope(xmlctx, "ok", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, &child);
+ if (r == LY_SUCCESS) {
+ /* insert */
+ lyd_insert_node(*envp, NULL, child);
+
+ /* finish child parsing */
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\" of \"ok\".",
+ xmlctx->name_len, xmlctx->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
+
+ /* success */
+ parsed_elem = "ok";
+ goto finish;
+ } else if (r != LY_ENOT) {
+ rc = r;
+ goto cleanup;
}
-cleanup:
- /* we have used parse_only flag */
- assert(!lydctx.node_types.count && !lydctx.meta_types.count && !lydctx.node_when.count);
- lyxml_ctx_free(lydctx.xmlctx);
- if (ret) {
- lyd_free_all(tree);
+ /* try to parse all "rpc-error" elements */
+ while (xmlctx->status == LYXML_ELEMENT) {
+ r = lydxml_envelope(xmlctx, "rpc-error", "urn:ietf:params:xml:ns:netconf:base:1.0", 0, &child);
+ if (r == LY_ENOT) {
+ break;
+ } else if (r) {
+ rc = r;
+ goto cleanup;
+ }
+
+ /* insert */
+ lyd_insert_node(*envp, NULL, child);
+
+ /* parse all children of "rpc-error" */
+ LY_CHECK_GOTO(rc = lydxml_env_netconf_rpc_reply_error(xmlctx, child), cleanup);
+
+ /* finish child parsing */
+ assert(xmlctx->status == LYXML_ELEM_CLOSE);
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(xmlctx), cleanup);
+
+ parsed_elem = "rpc-error";
}
- return ret;
+
+finish:
+ if (parsed_elem) {
+ /* NETCONF rpc-reply with no data */
+ if (xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(xmlctx->ctx, LYVE_SYNTAX, "Unexpected sibling element \"%.*s\" of \"%s\".",
+ xmlctx->name_len, xmlctx->name, parsed_elem);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ /* NETCONF rpc-reply */
+ *int_opts = LYD_INTOPT_NO_SIBLINGS | LYD_INTOPT_REPLY;
+ *close_elem = 1;
+
+cleanup:
+ if (rc) {
+ lyd_free_tree(*envp);
+ *envp = NULL;
+ }
+ return rc;
+}
+
+/**
+ * @brief Parse all expected non-data XML elements of a NETCONF rpc-reply or notification message.
+ *
+ * @param[in] xmlctx XML parser context.
+ * @param[out] evnp Parsed envelope(s) (opaque node).
+ * @param[out] int_opts Internal options for parsing the rest of YANG data.
+ * @param[out] close_elem Number of parsed opened elements that need to be closed.
+ * @param[out] parent_p Parent to append to the rest of YANG data, may be unset.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lydxml_env_netconf_reply_or_notif(struct lyxml_ctx *xmlctx, struct lyd_node **envp, uint32_t *int_opts,
+ uint32_t *close_elem, struct lyd_node **parent_p)
+{
+ /* is it a "rpc-reply" or a "notification"? */
+ assert(xmlctx->status == LYXML_ELEMENT);
+ if (!ly_strncmp("rpc-reply", xmlctx->name, xmlctx->name_len)) {
+ LY_CHECK_RET(lydxml_env_netconf_reply(xmlctx, envp, int_opts, close_elem));
+ } else if (!ly_strncmp("notification", xmlctx->name, xmlctx->name_len)) {
+ LY_CHECK_RET(lydxml_env_netconf_notif(xmlctx, envp, int_opts, close_elem));
+
+ /* unset parent, the notification starts from top-level */
+ *parent_p = NULL;
+ } else {
+ LOGVAL(xmlctx->ctx, LYVE_REFERENCE, "Unexpected element \"%.*s\" instead of \"rpc-reply\" or \"notification\".",
+ xmlctx->name_len, xmlctx->name);
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
}
LY_ERR
-lyd_parse_xml_reply(const struct ly_ctx *ctx, struct ly_in *in, struct lyd_node **tree_p, struct lyd_node **op_p)
+lyd_parse_xml(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in,
+ uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, struct lyd_node **envp, struct ly_set *parsed,
+ struct lyd_ctx **lydctx_p)
{
- LY_ERR ret = LY_SUCCESS;
- struct lyd_xml_ctx lydctx = {0};
- struct lyd_node *tree = NULL;
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_xml_ctx *lydctx;
+ uint32_t i, int_opts, close_elem = 0;
+ ly_bool parsed_data_nodes = 0;
- /* init */
- LY_CHECK_GOTO(ret = lyxml_ctx_new(ctx, in, &lydctx.xmlctx), cleanup);
- lydctx.parse_options = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
- lydctx.int_opts = LYD_INTOPT_REPLY;
+ assert(ctx && in && lydctx_p);
+ assert(!(parse_opts & ~LYD_PARSE_OPTS_MASK));
+ assert(!(val_opts & ~LYD_VALIDATE_OPTS_MASK));
-#if 0
- /* parse "rpc-reply", if any */
- LY_CHECK_GOTO(ret = lydxml_envelope(lydctx.xmlctx, "rpc-reply", "urn:ietf:params:xml:ns:netconf:base:1.0", &rpcr_e),
- cleanup);
-#endif
+ /* init context */
+ lydctx = calloc(1, sizeof *lydctx);
+ LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
+ LY_CHECK_GOTO(rc = lyxml_ctx_new(ctx, in, &lydctx->xmlctx), cleanup);
+ lydctx->parse_opts = parse_opts;
+ lydctx->val_opts = val_opts;
+ lydctx->free = lyd_xml_ctx_free;
- /* parse the rest of data normally but connect them to the duplicated operation */
- while (lydctx.xmlctx->status == LYXML_ELEMENT) {
- ret = lydxml_subtree_r(&lydctx, NULL, &tree);
- LY_CHECK_GOTO(ret, cleanup);
+ switch (data_type) {
+ case LYD_TYPE_YANG_DATA:
+ int_opts = LYD_INTOPT_WITH_SIBLINGS;
+ break;
+ case LYD_TYPE_YANG_RPC:
+ int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_YANG_NOTIF:
+ int_opts = LYD_INTOPT_NOTIF | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_YANG_REPLY:
+ int_opts = LYD_INTOPT_REPLY | LYD_INTOPT_NO_SIBLINGS;
+ break;
+ case LYD_TYPE_NETCONF_RPC:
+ assert(!parent);
+ LY_CHECK_GOTO(rc = lydxml_env_netconf_rpc(lydctx->xmlctx, envp, &int_opts, &close_elem), cleanup);
+ break;
+ case LYD_TYPE_NETCONF_REPLY_OR_NOTIF:
+ assert(parent);
+ LY_CHECK_GOTO(rc = lydxml_env_netconf_reply_or_notif(lydctx->xmlctx, envp, &int_opts, &close_elem, &parent),
+ cleanup);
+ break;
+ }
+ lydctx->int_opts = int_opts;
+
+ /* find the operation node if it exists already */
+ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
+
+ /* parse XML data */
+ while (lydctx->xmlctx->status == LYXML_ELEMENT) {
+ LY_CHECK_GOTO(rc = lydxml_subtree_r(lydctx, parent, first_p, parsed), cleanup);
+ parsed_data_nodes = 1;
+
+ if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
+ break;
+ }
}
- if (op_p) {
- *op_p = lydctx.op_node;
+ /* close all opened elements */
+ for (i = 0; i < close_elem; ++i) {
+ if (lydctx->xmlctx->status != LYXML_ELEM_CLOSE) {
+ assert(lydctx->xmlctx->status == LYXML_ELEMENT);
+ LOGVAL(lydctx->xmlctx->ctx, LYVE_SYNTAX, "Unexpected child element \"%.*s\".", lydctx->xmlctx->name_len,
+ lydctx->xmlctx->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ LY_CHECK_GOTO(rc = lyxml_ctx_next(lydctx->xmlctx), cleanup);
}
- if (tree_p) {
- *tree_p = tree;
+
+ /* check final state */
+ if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && (lydctx->xmlctx->status == LYXML_ELEMENT)) {
+ LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) {
+ LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (!parsed_data_nodes) {
+ /* no data nodes were parsed */
+ lydctx->op_node = NULL;
}
cleanup:
- /* we have used parse_only flag */
- assert(!lydctx.node_types.count && !lydctx.meta_types.count && !lydctx.node_when.count);
- lyxml_ctx_free(lydctx.xmlctx);
- if (ret) {
- lyd_free_all(tree);
+ /* there should be no unres stored if validation should be skipped */
+ assert(!(parse_opts & LYD_PARSE_ONLY) || (!lydctx->node_types.count && !lydctx->meta_types.count &&
+ !lydctx->node_when.count));
+
+ if (rc) {
+ lyd_xml_ctx_free((struct lyd_ctx *)lydctx);
+ } else {
+ *lydctx_p = (struct lyd_ctx *)lydctx;
+
+ /* the XML context is no more needed, freeing it also stops logging line numbers which would be confusing now */
+ lyxml_ctx_free(lydctx->xmlctx);
+ lydctx->xmlctx = NULL;
}
- return ret;
+ return rc;
}
diff --git a/src/tree_data.c b/src/tree_data.c
index 4230160..dbb5c94 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -295,102 +295,117 @@
return format;
}
-API LY_ERR
-lyd_parse_data(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options,
- struct lyd_node **tree)
+/**
+ * @brief Parse YANG data into a data tree.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] parent Parent to connect the parsed nodes to, if any.
+ * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL.
+ * @param[in] in Input handle to read the input from.
+ * @param[in] format Expected format of the data in @p in.
+ * @param[in] parse_opts Options for parser.
+ * @param[in] val_opts Options for validation.
+ * @param[out] op Optional pointer to the parsed operation, if any.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_parse(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in,
+ LYD_FORMAT format, uint32_t parse_opts, uint32_t val_opts, struct lyd_node **op)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR rc = LY_SUCCESS;
struct lyd_ctx *lydctx = NULL;
+ struct ly_set parsed = {0};
+ struct lyd_node *first;
+ uint32_t i;
- LY_CHECK_ARG_RET(ctx, ctx, in, tree, LY_EINVAL);
- LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL);
- LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL);
+ assert(ctx && (parent || first_p));
format = lyd_parse_get_format(in, format);
LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
-
- /* init */
- *tree = NULL;
+ if (first_p) {
+ *first_p = NULL;
+ }
/* remember input position */
in->func_start = in->current;
+ /* parse the data */
switch (format) {
case LYD_XML:
- LY_CHECK_RET(lyd_parse_xml_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+ rc = lyd_parse_xml(ctx, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_YANG_DATA, NULL, &parsed, &lydctx);
break;
case LYD_JSON:
- LY_CHECK_RET(lyd_parse_json_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+ rc = lyd_parse_json(ctx, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_YANG_DATA, &parsed, &lydctx);
break;
case LYD_LYB:
- LY_CHECK_RET(lyd_parse_lyb_data(ctx, in, parse_options, validate_options, tree, &lydctx));
+ rc = lyd_parse_lyb(ctx, parent, first_p, in, parse_opts, val_opts, LYD_TYPE_YANG_DATA, &parsed, &lydctx);
break;
case LYD_UNKNOWN:
- LOGINT_RET(ctx);
+ LOGINT(ctx);
+ rc = LY_EINT;
+ break;
+ }
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if (parent) {
+ /* get first top-level sibling */
+ for (first = parent; first->parent; first = lyd_parent(first)) {}
+ first = lyd_first_sibling(first);
+ first_p = &first;
}
- if (!(parse_options & LYD_PARSE_ONLY)) {
- uint32_t i = 0;
- const struct lys_module *mod;
- struct lyd_node *first, *next, **first2;
+ if (!(parse_opts & LYD_PARSE_ONLY)) {
+ /* validate data */
+ rc = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types,
+ &lydctx->meta_types, NULL);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
- next = *tree;
- while (1) {
- if (validate_options & LYD_VALIDATE_PRESENT) {
- mod = lyd_data_next_module(&next, &first);
- } else {
- mod = lyd_mod_next_module(next, NULL, ctx, &i, &first);
- }
- if (!mod) {
- break;
- }
- if (!first || (first == *tree)) {
- /* make sure first2 changes are carried to tree */
- first2 = tree;
- } else {
- first2 = &first;
- }
-
- /* validate new top-level nodes, autodelete CANNOT occur, all nodes are new */
- LY_CHECK_GOTO(ret = lyd_validate_new(first2, NULL, mod, NULL), cleanup);
-
- /* add all top-level defaults for this module */
- ret = lyd_new_implicit_r(NULL, first2, NULL, mod, &lydctx->node_types, &lydctx->node_when,
- (validate_options & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* our first module node pointer may no longer be the first */
- while (*first2 && (*first2)->prev->next && (lyd_owner_module(*first2) == lyd_owner_module((*first2)->prev))) {
- *first2 = (*first2)->prev;
- }
-
- /* finish incompletely validated terminal values/attributes and when conditions */
- ret = lyd_validate_unres(first2, mod, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types, NULL);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* perform final validation that assumes the data tree is final */
- LY_CHECK_GOTO(ret = lyd_validate_final_r(*first2, NULL, NULL, mod, validate_options, 0), cleanup);
- }
+ /* set the operation node */
+ if (op) {
+ *op = lydctx->op_node;
}
cleanup:
- lydctx->free(lydctx);
- if (ret) {
- lyd_free_all(*tree);
- *tree = NULL;
+ if (lydctx) {
+ lydctx->free(lydctx);
}
- return ret;
+ if (rc) {
+ if (parent) {
+ /* free all the parsed subtrees */
+ for (i = 0; i < parsed.count; ++i) {
+ lyd_free_tree(parsed.dnodes[i]);
+ }
+ } else {
+ /* free everything */
+ lyd_free_all(*first_p);
+ *first_p = NULL;
+ }
+ }
+ ly_set_erase(&parsed, NULL);
+ return rc;
}
API LY_ERR
-lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options,
- struct lyd_node **tree)
+lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+ uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree)
+{
+ LY_CHECK_ARG_RET(ctx, ctx, in, parent || tree, LY_EINVAL);
+ LY_CHECK_ARG_RET(ctx, !(parse_options & ~LYD_PARSE_OPTS_MASK), LY_EINVAL);
+ LY_CHECK_ARG_RET(ctx, !(validate_options & ~LYD_VALIDATE_OPTS_MASK), LY_EINVAL);
+
+ return lyd_parse(ctx, parent, tree, in, format, parse_options, validate_options, NULL);
+}
+
+API LY_ERR
+lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options,
+ uint32_t validate_options, struct lyd_node **tree)
{
LY_ERR ret;
struct ly_in *in;
LY_CHECK_RET(ly_in_new_memory(data, &in));
- ret = lyd_parse_data(ctx, in, format, parse_options, validate_options, tree);
+ ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree);
ly_in_free(in, 0);
return ret;
@@ -404,7 +419,7 @@
struct ly_in *in;
LY_CHECK_RET(ly_in_new_fd(fd, &in));
- ret = lyd_parse_data(ctx, in, format, parse_options, validate_options, tree);
+ ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree);
ly_in_free(in, 0);
return ret;
@@ -418,53 +433,23 @@
struct ly_in *in;
LY_CHECK_RET(ly_in_new_filepath(path, 0, &in));
- ret = lyd_parse_data(ctx, in, format, parse_options, validate_options, tree);
+ ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree);
ly_in_free(in, 0);
return ret;
}
API LY_ERR
-lyd_parse_rpc(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree, struct lyd_node **op)
+lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in, LYD_FORMAT format,
+ enum lyd_type data_type, struct lyd_node **tree, struct lyd_node **op)
{
- LY_CHECK_ARG_RET(ctx, ctx, in, tree, LY_EINVAL);
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_ctx *lydctx = NULL;
+ struct ly_set parsed = {0};
+ struct lyd_node *first = NULL, *envp = NULL;
+ uint32_t i, parse_opts, val_opts;
- format = lyd_parse_get_format(in, format);
- LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
-
- /* init */
- *tree = NULL;
- if (op) {
- *op = NULL;
- }
-
- /* remember input position */
- in->func_start = in->current;
-
- switch (format) {
- case LYD_XML:
- return lyd_parse_xml_rpc(ctx, in, tree, op);
- case LYD_JSON:
- return lyd_parse_json_rpc(ctx, in, tree, op);
- case LYD_LYB:
- return lyd_parse_lyb_rpc(ctx, in, tree, op);
- case LYD_UNKNOWN:
- break;
- }
-
- LOGINT_RET(ctx);
-}
-
-API LY_ERR
-lyd_parse_reply(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree,
- struct lyd_node **op)
-{
- LY_CHECK_ARG_RET(ctx, ctx, in, tree || op, LY_EINVAL);
-
- format = lyd_parse_get_format(in, format);
- LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
-
- /* init */
+ LY_CHECK_ARG_RET(ctx, ctx || parent, in, data_type, parent || tree || op, LY_EINVAL);
if (tree) {
*tree = NULL;
}
@@ -472,54 +457,75 @@
*op = NULL;
}
- /* remember input position */
- in->func_start = in->current;
-
- switch (format) {
- case LYD_XML:
- return lyd_parse_xml_reply(ctx, in, tree, op);
- case LYD_JSON:
- return lyd_parse_json_reply(ctx, in, tree, op);
- case LYD_LYB:
- return lyd_parse_lyb_reply(ctx, in, tree, op);
- case LYD_UNKNOWN:
- break;
- }
-
- LOGINT_RET(ctx);
-}
-
-API LY_ERR
-lyd_parse_notif(const struct ly_ctx *ctx, struct ly_in *in, LYD_FORMAT format, struct lyd_node **tree, struct lyd_node **ntf)
-{
- LY_CHECK_ARG_RET(ctx, ctx, in, tree || ntf, LY_EINVAL);
-
format = lyd_parse_get_format(in, format);
LY_CHECK_ARG_RET(ctx, format, LY_EINVAL);
- /* init */
- if (tree) {
- *tree = NULL;
- }
- if (ntf) {
- *ntf = NULL;
- }
-
/* remember input position */
in->func_start = in->current;
+ /* check params based on the data type */
+ if (data_type == LYD_TYPE_NETCONF_RPC) {
+ LY_CHECK_ARG_RET(ctx, format == LYD_XML, !parent, tree, op, LY_EINVAL);
+ } else if (data_type == LYD_TYPE_NETCONF_REPLY_OR_NOTIF) {
+ LY_CHECK_ARG_RET(ctx, format == LYD_XML, parent, parent->schema->nodetype & (LYS_RPC | LYS_ACTION), tree, op, LY_EINVAL);
+ }
+ parse_opts = LYD_PARSE_ONLY | LYD_PARSE_STRICT;
+ val_opts = 0;
+
+ /* parse the data */
switch (format) {
case LYD_XML:
- return lyd_parse_xml_notif(ctx, in, tree, ntf);
+ rc = lyd_parse_xml(ctx, parent, &first, in, parse_opts, val_opts, data_type, &envp, &parsed, &lydctx);
+ break;
case LYD_JSON:
- return lyd_parse_json_notif(ctx, in, tree, ntf);
+ rc = lyd_parse_json(ctx, parent, &first, in, parse_opts, val_opts, data_type, &parsed, &lydctx);
+ break;
case LYD_LYB:
- return lyd_parse_lyb_notif(ctx, in, tree, ntf);
+ rc = lyd_parse_lyb(ctx, parent, &first, in, parse_opts, val_opts, data_type, &parsed, &lydctx);
+ break;
case LYD_UNKNOWN:
+ LOGINT(ctx);
+ rc = LY_EINT;
break;
}
+ LY_CHECK_GOTO(rc, cleanup);
- LOGINT_RET(ctx);
+ /* set out params correctly */
+ if (tree) {
+ if (envp) {
+ /* special out param meaning */
+ *tree = envp;
+ } else {
+ *tree = parent ? NULL : first;
+ }
+ }
+ if (op) {
+ *op = lydctx->op_node;
+ }
+
+cleanup:
+ if (lydctx) {
+ lydctx->free(lydctx);
+ }
+ if (rc) {
+ if (parent) {
+ /* free all the parsed subtrees */
+ for (i = 0; i < parsed.count; ++i) {
+ lyd_free_tree(parsed.dnodes[i]);
+ }
+ } else {
+ /* free everything (cannot occur in the current code, a safety) */
+ lyd_free_all(first);
+ if (tree) {
+ *tree = NULL;
+ }
+ if (op) {
+ *op = NULL;
+ }
+ }
+ }
+ ly_set_erase(&parsed, NULL);
+ return rc;
}
LY_ERR
@@ -1586,7 +1592,7 @@
}
/* create any default children */
- LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, node_types, node_when,
+ LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, node_types, node_when,
impl_opts, diff));
}
break;
@@ -1670,7 +1676,7 @@
/* skip added default nodes */
if (((node->flags & (LYD_DEFAULT | LYD_NEW)) != (LYD_DEFAULT | LYD_NEW)) &&
(node->schema->nodetype & LYD_NODE_INNER)) {
- LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, NULL,
+ LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL,
&node_when, implicit_options, diff), cleanup);
}
@@ -2995,7 +3001,7 @@
} else {
/* check descendants, recursively */
LY_LIST_FOR_SAFE(lyd_child_no_keys(sibling_src), tmp, child_src) {
- LY_CHECK_RET(lyd_merge_sibling_r(lyd_node_children_p(match_trg), match_trg, &child_src, options));
+ LY_CHECK_RET(lyd_merge_sibling_r(lyd_node_child_p(match_trg), match_trg, &child_src, options));
}
}
} else {
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index ab4b0e5..7aaf1fb 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -66,7 +66,7 @@
}
struct lyd_node **
-lyd_node_children_p(struct lyd_node *node)
+lyd_node_child_p(struct lyd_node *node)
{
assert(node);
@@ -100,7 +100,7 @@
return ((struct lyd_node_opaq *)node)->child;
}
- children = lyd_node_children_p((struct lyd_node *)node);
+ children = lyd_node_child_p((struct lyd_node *)node);
if (children) {
struct lyd_node *child = *children;
while (child && child->schema && (child->schema->flags & LYS_KEY)) {
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index ce690bb..bf06c4b 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -34,13 +34,6 @@
#define LY_LYB_SUFFIX_LEN 4
/**
- * @brief Internal data parser flags.
- */
-#define LYD_INTOPT_RPC 0x01 /**< RPC/action invocation is being parsed */
-#define LYD_INTOPT_NOTIF 0x02 /**< notification is being parsed */
-#define LYD_INTOPT_REPLY 0x04 /**< RPC/action reply is being parsed */
-
-/**
* @brief Hash schema sibling to be used for LYB data.
*
* @param[in] sibling Sibling to hash.
@@ -76,7 +69,7 @@
* @return Address of the node's child member,
* @return NULL if there is no child pointer.
*/
-struct lyd_node **lyd_node_children_p(struct lyd_node *node);
+struct lyd_node **lyd_node_child_p(struct lyd_node *node);
/**
* @brief Just like ::lys_getnext() but iterates over all data instances of the schema nodes.
diff --git a/src/validation.c b/src/validation.c
index 6427727..cb24d54 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -1057,12 +1057,12 @@
* @param[in] sparent Schema parent of the nodes to check.
* @param[in] mod Module of the nodes to check.
* @param[in] val_opts Validation options, see @ref datavalidationoptions.
- * @param[in] op Operation being validated, if any.
+ * @param[in] int_opts Internal parser options.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_node *parent,
- const struct lysc_node *sparent, const struct lysc_module *mod, uint32_t val_opts, LYD_VALIDATE_OP op)
+ const struct lysc_node *sparent, const struct lysc_module *mod, uint32_t val_opts, uint32_t int_opts)
{
LY_ERR ret = LY_SUCCESS;
const struct lysc_node *snode = NULL, *scase;
@@ -1070,7 +1070,7 @@
struct lysc_node_leaflist *sllist;
uint32_t getnext_opts;
- getnext_opts = LYS_GETNEXT_WITHCHOICE | (op == LYD_VALIDATE_OP_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+ getnext_opts = LYS_GETNEXT_WITHCHOICE | (int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
/* disabled nodes are skipped by lys_getnext */
while ((snode = lys_getnext(snode, sparent, mod, getnext_opts))) {
@@ -1114,7 +1114,7 @@
LY_LIST_FOR(lysc_node_child(snode), scase) {
if (lys_getnext_data(NULL, first, NULL, scase, NULL)) {
/* validate only this case */
- ret = lyd_validate_siblings_schema_r(first, parent, scase, mod, val_opts, op);
+ ret = lyd_validate_siblings_schema_r(first, parent, scase, mod, val_opts, int_opts);
LY_CHECK_GOTO(ret, error);
break;
}
@@ -1156,11 +1156,11 @@
* @brief Validate must conditions of a data node.
*
* @param[in] node Node to validate.
- * @param[in] op Operation being validated, if any.
+ * @param[in] int_opts Internal parser options.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_must(const struct lyd_node *node, LYD_VALIDATE_OP op)
+lyd_validate_must(const struct lyd_node *node, uint32_t int_opts)
{
struct lyxp_set xp_set;
struct lysc_must *musts;
@@ -1168,10 +1168,13 @@
const struct lysc_node *schema;
LY_ARRAY_COUNT_TYPE u;
+ assert((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_REPLY)) != (LYD_INTOPT_RPC | LYD_INTOPT_REPLY));
+ assert((int_opts & (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) != (LYD_INTOPT_ACTION | LYD_INTOPT_REPLY));
+
if (node->schema->nodetype & (LYS_ACTION | LYS_RPC)) {
- if (op == LYD_VALIDATE_OP_RPC) {
+ if (int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION)) {
schema = &((struct lysc_node_action *)node->schema)->input.node;
- } else if (op == LYD_VALIDATE_OP_REPLY) {
+ } else if (int_opts & LYD_INTOPT_REPLY) {
schema = &((struct lysc_node_action *)node->schema)->output.node;
} else {
LOGINT(LYD_CTX(node));
@@ -1208,9 +1211,20 @@
return LY_SUCCESS;
}
-LY_ERR
+/**
+ * @brief Perform all remaining validation tasks, the data tree must be final when calling this function.
+ *
+ * @param[in] first First sibling.
+ * @param[in] parent Data parent.
+ * @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in] val_opts Validation options (@ref datavalidationoptions).
+ * @param[in] int_opts Internal parser options.
+ * @return LY_ERR value.
+ */
+static LY_ERR
lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *sparent,
- const struct lys_module *mod, uint32_t val_opts, LYD_VALIDATE_OP op)
+ const struct lys_module *mod, uint32_t val_opts, uint32_t int_opts)
{
const char *innode = NULL;
struct lyd_node *next = NULL, *node;
@@ -1235,10 +1249,10 @@
if ((val_opts & LYD_VALIDATE_NO_STATE) && (node->schema->flags & LYS_CONFIG_R)) {
innode = "state";
goto invalid_node;
- } else if ((op == LYD_VALIDATE_OP_RPC) && (node->schema->flags & LYS_IS_OUTPUT)) {
+ } else if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION)) && (node->schema->flags & LYS_IS_OUTPUT)) {
innode = "output";
goto invalid_node;
- } else if ((op == LYD_VALIDATE_OP_REPLY) && (node->schema->flags & LYS_IS_INPUT)) {
+ } else if ((int_opts & LYD_INTOPT_REPLY) && (node->schema->flags & LYS_IS_INPUT)) {
innode = "input";
goto invalid_node;
}
@@ -1247,7 +1261,7 @@
lyd_validate_obsolete(node);
/* node's musts */
- LY_CHECK_RET(lyd_validate_must(node, op));
+ LY_CHECK_RET(lyd_validate_must(node, int_opts));
/* node value was checked by plugins */
@@ -1255,11 +1269,11 @@
}
/* validate schema-based restrictions */
- LY_CHECK_RET(lyd_validate_siblings_schema_r(first, parent, sparent, mod ? mod->compiled : NULL, val_opts, op));
+ LY_CHECK_RET(lyd_validate_siblings_schema_r(first, parent, sparent, mod ? mod->compiled : NULL, val_opts, int_opts));
LY_LIST_FOR(first, node) {
/* validate all children recursively */
- LY_CHECK_RET(lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, val_opts, op));
+ LY_CHECK_RET(lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, val_opts, int_opts));
/* set default for containers */
if ((node->schema->nodetype == LYS_CONTAINER) && !(node->schema->flags & LYS_PRESENCE)) {
@@ -1286,16 +1300,16 @@
* @brief Validate the whole data subtree.
*
* @param[in] root Subtree root.
+ * @param[in,out] node_when Set for nodes with when conditions.
* @param[in,out] node_types Set for unres node types.
* @param[in,out] meta_types Set for unres metadata types.
- * @param[in,out] node_when Set for nodes with when conditions.
* @param[in] impl_opts Implicit options, see @ref implicitoptions.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
-lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_types, struct ly_set *meta_types,
- struct ly_set *node_when, uint32_t impl_opts, struct lyd_node **diff)
+lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_set *node_types,
+ struct ly_set *meta_types, uint32_t impl_opts, struct lyd_node **diff)
{
const struct lyd_meta *meta;
struct lyd_node *node;
@@ -1313,10 +1327,10 @@
LY_CHECK_RET(ly_set_add(node_types, (void *)node, 1, NULL));
} else if (node->schema->nodetype & LYD_NODE_INNER) {
/* new node validation, autodelete */
- LY_CHECK_RET(lyd_validate_new(lyd_node_children_p(node), node->schema, NULL, diff));
+ LY_CHECK_RET(lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, diff));
/* add nested defaults */
- LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_children_p(node), NULL, NULL, NULL, NULL, impl_opts, diff));
+ LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, impl_opts, diff));
}
if (lysc_node_when(node->schema)) {
@@ -1330,19 +1344,9 @@
return LY_SUCCESS;
}
-/**
- * @brief Validate data tree.
- *
- * @param[in,out] tree Data tree to validate, nodes may be autodeleted.
- * @param[in] modules Array of modules to validate, NULL for all modules.
- * @param[in] mod_count Count of @p modules.
- * @param[in] ly_ctx libyang context.
- * @param[in] val_opts Validation options, see @ref datavalidationoptions.
- * @param[out] diff Generated validation diff, not generated if NULL.
- * @return LY_ERR value.
- */
-static LY_ERR
+LY_ERR
lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, uint32_t val_opts,
+ ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
@@ -1351,12 +1355,13 @@
struct ly_set node_types = {0}, meta_types = {0}, node_when = {0};
uint32_t i = 0;
- LY_CHECK_ARG_RET(NULL, tree, *tree || ctx || module, LY_EINVAL);
- if (!ctx && !module) {
- ctx = LYD_CTX(*tree);
- }
- if (diff) {
- *diff = NULL;
+ assert(tree && ctx);
+ assert((node_when_p && node_types_p && meta_types_p) || (!node_when_p && !node_types_p && !meta_types_p));
+
+ if (!node_when_p) {
+ node_when_p = &node_when;
+ node_types_p = &node_types;
+ meta_types_p = &meta_types;
}
next = *tree;
@@ -1381,7 +1386,7 @@
LY_CHECK_GOTO(ret, cleanup);
/* add all top-level defaults for this module, do not add into unres sets, will occur in the next step */
- ret = lyd_new_implicit_r(NULL, first2, NULL, mod, NULL, NULL, val_opts & LYD_VALIDATE_NO_STATE ?
+ ret = lyd_new_implicit_r(NULL, first2, NULL, mod, NULL, NULL, (val_opts & LYD_VALIDATE_NO_STATE) ?
LYD_IMPLICIT_NO_STATE : 0, diff);
LY_CHECK_GOTO(ret, cleanup);
@@ -1390,15 +1395,17 @@
*first2 = (*first2)->prev;
}
- /* process nested nodes */
- LY_LIST_FOR(*first2, iter) {
- ret = lyd_validate_subtree(iter, &node_types, &meta_types, &node_when, val_opts & LYD_VALIDATE_NO_STATE ?
- LYD_IMPLICIT_NO_STATE : 0, diff);
- LY_CHECK_GOTO(ret, cleanup);
+ if (validate_subtree) {
+ /* process nested nodes */
+ LY_LIST_FOR(*first2, iter) {
+ ret = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p,
+ (val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, diff);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
}
/* finish incompletely validated terminal values/attributes and when conditions */
- ret = lyd_validate_unres(first2, mod, &node_when, &node_types, &meta_types, diff);
+ ret = lyd_validate_unres(first2, mod, node_when_p, node_types_p, meta_types_p, diff);
LY_CHECK_GOTO(ret, cleanup);
/* perform final validation that assumes the data tree is final */
@@ -1407,22 +1414,35 @@
}
cleanup:
+ ly_set_erase(&node_when, NULL);
ly_set_erase(&node_types, NULL);
ly_set_erase(&meta_types, NULL);
- ly_set_erase(&node_when, NULL);
return ret;
}
API LY_ERR
lyd_validate_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t val_opts, struct lyd_node **diff)
{
- return lyd_validate(tree, NULL, ctx, val_opts, diff);
+ LY_CHECK_ARG_RET(NULL, tree, *tree || ctx, LY_EINVAL);
+ if (!ctx) {
+ ctx = LYD_CTX(*tree);
+ }
+ if (diff) {
+ *diff = NULL;
+ }
+
+ return lyd_validate(tree, NULL, ctx, val_opts, 1, NULL, NULL, NULL, diff);
}
API LY_ERR
lyd_validate_module(struct lyd_node **tree, const struct lys_module *module, uint32_t val_opts, struct lyd_node **diff)
{
- return lyd_validate(tree, module, NULL, val_opts, diff);
+ LY_CHECK_ARG_RET(NULL, tree, *tree || module, LY_EINVAL);
+ if (diff) {
+ *diff = NULL;
+ }
+
+ return lyd_validate(tree, module, (*tree) ? LYD_CTX(*tree) : module->ctx, val_opts, 1, NULL, NULL, NULL, diff);
}
/**
@@ -1488,29 +1508,124 @@
}
}
-API LY_ERR
-lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *tree, LYD_VALIDATE_OP op, struct lyd_node **diff)
+/**
+ * @brief Validate an RPC/action request, reply, or notification.
+ *
+ * @param[in] op_tree Full operation data tree.
+ * @param[in] op_node Operation node itself.
+ * @param[in] dep_tree Tree to be used for validating references from the operation subtree.
+ * @param[in] int_opts Internal parser options.
+ * @param[in] validate_subtree Whether subtree was already validated (as part of data parsing) or not (separate validation).
+ * @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
+ * @param[in] node_types_p Set of unres node types, if NULL a local set is used.
+ * @param[in] meta_types_p Set of unres metadata types, if NULL a local set is used.
+ * @param[out] diff Optional diff with any changes made by the validation.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR error on error.
+ */
+static LY_ERR
+_lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struct lyd_node *dep_tree,
+ uint32_t int_opts, ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p,
+ struct ly_set *meta_types_p, struct lyd_node **diff)
{
- LY_ERR ret;
- struct lyd_node *tree_sibling, *tree_parent, *op_subtree, *op_node, *op_parent, *child;
- struct ly_set type_check = {0}, type_meta_check = {0}, when_check = {0};
+ LY_ERR rc = LY_SUCCESS;
+ struct lyd_node *tree_sibling, *tree_parent, *op_subtree, *op_parent, *child;
+ struct ly_set node_types = {0}, meta_types = {0}, node_when = {0};
- LY_CHECK_ARG_RET(NULL, op_tree, !op_tree->parent, !tree || !tree->parent,
- (op == LYD_VALIDATE_OP_NOTIF) || (op == LYD_VALIDATE_OP_RPC) || (op == LYD_VALIDATE_OP_REPLY), LY_EINVAL);
+ assert(op_tree && op_node);
+ assert((node_when_p && node_types_p && meta_types_p) || (!node_when_p && !node_types_p && !meta_types_p));
+
+ if (!node_when_p) {
+ node_when_p = &node_when;
+ node_types_p = &node_types;
+ meta_types_p = &meta_types;
+ }
+
+ /* merge op_tree into dep_tree */
+ lyd_val_op_merge_find(op_tree, op_node, dep_tree, &op_subtree, &tree_sibling, &tree_parent);
+ op_parent = lyd_parent(op_subtree);
+ lyd_unlink_tree(op_subtree);
+ lyd_insert_node(tree_parent, &tree_sibling, op_subtree);
+ if (!dep_tree) {
+ dep_tree = tree_sibling;
+ }
+
+ LOG_LOCSET(NULL, op_node, NULL, NULL);
+
+ if (int_opts & LYD_INTOPT_REPLY) {
+ /* add output children defaults */
+ rc = lyd_new_implicit_r(op_node, lyd_node_child_p(op_node), NULL, NULL, node_types_p, node_when_p,
+ LYD_IMPLICIT_OUTPUT, diff);
+ LY_CHECK_GOTO(rc, cleanup);
+
+ if (validate_subtree) {
+ /* skip validating the operation itself, go to children directly */
+ LY_LIST_FOR(lyd_child(op_node), child) {
+ LY_CHECK_GOTO(rc = lyd_validate_subtree(child, node_when_p, node_types_p, meta_types_p, 0, diff), cleanup);
+ }
+ }
+ } else {
+ if (validate_subtree) {
+ /* prevalidate whole operation subtree */
+ LY_CHECK_GOTO(rc = lyd_validate_subtree(op_node, node_when_p, node_types_p, meta_types_p, 0, diff), cleanup);
+ }
+ }
+
+ /* finish incompletely validated terminal values/attributes and when conditions on the full tree */
+ LY_CHECK_GOTO(rc = lyd_validate_unres((struct lyd_node **)&dep_tree, NULL, node_when_p, node_types_p, meta_types_p,
+ diff), cleanup);
+
+ /* perform final validation of the operation/notification */
+ lyd_validate_obsolete(op_node);
+ LY_CHECK_GOTO(rc = lyd_validate_must(op_node, int_opts), cleanup);
+
+ /* final validation of all the descendants */
+ LY_CHECK_GOTO(rc = lyd_validate_final_r(lyd_child(op_node), op_node, op_node->schema, NULL, 0, int_opts), cleanup);
+
+cleanup:
+ LOG_LOCBACK(0, 1, 0, 0);
+ /* restore operation tree */
+ lyd_unlink_tree(op_subtree);
+ if (op_parent) {
+ lyd_insert_node(op_parent, NULL, op_subtree);
+ }
+
+ ly_set_erase(&node_when, NULL);
+ ly_set_erase(&node_types, NULL);
+ ly_set_erase(&meta_types, NULL);
+ return rc;
+}
+
+API LY_ERR
+lyd_validate_op(struct lyd_node *op_tree, const struct lyd_node *dep_tree, enum lyd_type data_type, struct lyd_node **diff)
+{
+ struct lyd_node *op_node;
+ uint32_t int_opts;
+
+ LY_CHECK_ARG_RET(NULL, op_tree, !op_tree->parent, !dep_tree || !dep_tree->parent, (data_type == LYD_TYPE_YANG_RPC) ||
+ (data_type == LYD_TYPE_YANG_NOTIF) || (data_type == LYD_TYPE_YANG_REPLY), LY_EINVAL);
if (diff) {
*diff = NULL;
}
+ if (data_type == LYD_TYPE_YANG_RPC) {
+ int_opts = LYD_INTOPT_RPC | LYD_INTOPT_ACTION;
+ } else if (data_type == LYD_TYPE_YANG_NOTIF) {
+ int_opts = LYD_INTOPT_NOTIF;
+ } else {
+ int_opts = LYD_INTOPT_REPLY;
+ }
/* find the operation/notification */
LYD_TREE_DFS_BEGIN(op_tree, op_node) {
- if (((op == LYD_VALIDATE_OP_RPC) || (op == LYD_VALIDATE_OP_REPLY)) && (op_node->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
+ if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) &&
+ (op_node->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
break;
- } else if ((op == LYD_VALIDATE_OP_NOTIF) && (op_node->schema->nodetype == LYS_NOTIF)) {
+ } else if ((int_opts & LYD_INTOPT_NOTIF) && (op_node->schema->nodetype == LYS_NOTIF)) {
break;
}
LYD_TREE_DFS_END(op_tree, op_node);
}
- if ((op == LYD_VALIDATE_OP_RPC) || (op == LYD_VALIDATE_OP_REPLY)) {
+ if (int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_REPLY)) {
if (!(op_node->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
LOGERR(LYD_CTX(op_tree), LY_EINVAL, "No RPC/action to validate found.");
return LY_EINVAL;
@@ -1522,56 +1637,6 @@
}
}
- /* move op_tree to top-level node */
- while (op_tree->parent) {
- op_tree = lyd_parent(op_tree);
- }
-
- /* merge op_tree into tree */
- lyd_val_op_merge_find(op_tree, op_node, tree, &op_subtree, &tree_sibling, &tree_parent);
- op_parent = lyd_parent(op_subtree);
- lyd_unlink_tree(op_subtree);
- lyd_insert_node(tree_parent, &tree_sibling, op_subtree);
- if (!tree) {
- tree = tree_sibling;
- }
-
- LOG_LOCSET(NULL, op_node, NULL, NULL);
-
- if (op == LYD_VALIDATE_OP_REPLY) {
- /* add output children defaults */
- LY_CHECK_RET(lyd_new_implicit_r(op_node, lyd_node_children_p(op_node), NULL, NULL, NULL, NULL, LYD_IMPLICIT_OUTPUT, diff));
-
- /* skip validating the operation itself, go to children directly */
- LY_LIST_FOR(lyd_child(op_node), child) {
- LY_CHECK_GOTO(ret = lyd_validate_subtree(child, &type_check, &type_meta_check, &when_check, 0, diff), cleanup);
- }
- } else {
- /* prevalidate whole operation subtree */
- LY_CHECK_GOTO(ret = lyd_validate_subtree(op_node, &type_check, &type_meta_check, &when_check, 0, diff), cleanup);
- }
-
- /* finish incompletely validated terminal values/attributes and when conditions on the full tree */
- LY_CHECK_GOTO(ret = lyd_validate_unres((struct lyd_node **)&tree, NULL, &when_check, &type_check, &type_meta_check,
- diff), cleanup);
-
- /* perform final validation of the operation/notification */
- lyd_validate_obsolete(op_node);
- LY_CHECK_GOTO(ret = lyd_validate_must(op_node, op), cleanup);
-
- /* final validation of all the descendants */
- LY_CHECK_GOTO(ret = lyd_validate_final_r(lyd_child(op_node), op_node, op_node->schema, NULL, 0, op), cleanup);
-
-cleanup:
- LOG_LOCBACK(0, 1, 0, 0);
- /* restore operation tree */
- lyd_unlink_tree(op_subtree);
- if (op_parent) {
- lyd_insert_node(op_parent, NULL, op_subtree);
- }
-
- ly_set_erase(&type_check, NULL);
- ly_set_erase(&type_meta_check, NULL);
- ly_set_erase(&when_check, NULL);
- return ret;
+ /* validate */
+ return _lyd_validate_op(op_tree, op_node, dep_tree, int_opts, 1, NULL, NULL, NULL, diff);
}
diff --git a/src/validation.h b/src/validation.h
index ae286be..c255270 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -70,17 +70,21 @@
struct lyd_node **diff);
/**
- * @brief Perform all remaining validation tasks, the data tree must be final when calling this function.
+ * @brief Validate a data tree.
*
- * @param[in] first First sibling.
- * @param[in] parent Data parent.
- * @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
- * @param[in] mod Module of the siblings, NULL for nested siblings.
- * @param[in] val_opts Validation options (@ref datavalidationoptions).
- * @param[in] op Operation to validate (@ref datavalidateop) or 0 for data tree
+ * @param[in,out] tree Data tree to validate, nodes may be autodeleted.
+ * @param[in] module Module whose data (and schema restrictions) to validate, NULL for all modules.
+ * @param[in] ctx libyang context.
+ * @param[in] val_opts Validation options, see @ref datavalidationoptions.
+ * @param[in] validate_subtree Whether subtree was already validated (as part of data parsing) or not (separate validation).
+ * @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
+ * @param[in] node_types_p Set of unres node types, if NULL a local set is used.
+ * @param[in] meta_types_p Set of unres metadata types, if NULL a local set is used.
+ * @param[out] diff Generated validation diff, not generated if NULL.
* @return LY_ERR value.
*/
-LY_ERR lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *sparent,
- const struct lys_module *mod, uint32_t val_opts, LYD_VALIDATE_OP op);
+LY_ERR lyd_validate(struct lyd_node **tree, const struct lys_module *module, const struct ly_ctx *ctx, uint32_t val_opts,
+ ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
+ struct lyd_node **diff);
#endif /* LY_VALIDATION_H_ */
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
index cca266c..fba4d53 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -436,7 +436,7 @@
"\"a:l1\":[{\"@\":{\"ietf-netconf:operation\":\"replace\"},\"a\":\"val_a\",\"b\":\"val_b\",\"c\":\"val_c\"}]}"
"}}";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_rpc(UTEST_LYCTX, in, LYD_JSON, &tree, &op));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_YANG_RPC, &tree, &op));
ly_in_free(in, 0);
assert_non_null(op);
@@ -479,7 +479,7 @@
data = "{\"a:c\":{\"act\":{\"al\":\"value\"}}}";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_rpc(UTEST_LYCTX, in, LYD_JSON, &tree, &op));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_YANG_RPC, &tree, &op));
ly_in_free(in, 0);
assert_non_null(op);
@@ -503,7 +503,7 @@
data = "{\"a:c\":{\"n1\":{\"nl\":\"value\"}}}";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_notif(UTEST_LYCTX, in, LYD_JSON, &tree, &ntf));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_YANG_NOTIF, &tree, &ntf));
ly_in_free(in, 0);
assert_non_null(ntf);
@@ -516,7 +516,7 @@
data = "{\"a:n2\":{}}";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_notif(UTEST_LYCTX, in, LYD_JSON, &tree, &ntf));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_YANG_NOTIF, &tree, &ntf));
ly_in_free(in, 0);
assert_non_null(ntf);
@@ -542,7 +542,7 @@
data = "{\"a:c\":{\"act\":{\"al\":25}}}";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_reply(UTEST_LYCTX, in, LYD_JSON, &tree, &op));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_JSON, LYD_TYPE_YANG_REPLY, &tree, &op));
ly_in_free(in, 0);
assert_non_null(op);
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index b2e2805..35b0e09 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -321,7 +321,7 @@
" </config>\n"
"</edit-config>\n";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_rpc(UTEST_LYCTX, in, LYD_XML, &tree, &op));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_YANG_RPC, &tree, &op));
ly_in_free(in, 0);
assert_non_null(op);
@@ -387,7 +387,7 @@
" </act>\n"
"</c>\n";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_rpc(UTEST_LYCTX, in, LYD_XML, &tree, &op));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_YANG_RPC, &tree, &op));
ly_in_free(in, 0);
assert_non_null(op);
@@ -421,7 +421,7 @@
" </n1>\n"
"</c>\n";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_notif(UTEST_LYCTX, in, LYD_XML, &tree, &ntf));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_YANG_NOTIF, &tree, &ntf));
ly_in_free(in, 0);
assert_non_null(ntf);
@@ -435,7 +435,7 @@
/* top-level notif without envelope */
data = "<n2 xmlns=\"urn:tests:a\"/>\n";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_notif(UTEST_LYCTX, in, LYD_XML, &tree, &ntf));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_YANG_NOTIF, &tree, &ntf));
ly_in_free(in, 0);
assert_non_null(ntf);
@@ -465,7 +465,7 @@
" </act>\n"
"</c>\n";
assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_reply(UTEST_LYCTX, in, LYD_XML, &tree, &op));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_YANG_REPLY, &tree, &op));
ly_in_free(in, 0);
assert_non_null(op);
@@ -486,6 +486,260 @@
/* TODO */
}
+static void
+test_netconf_rpc(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+ const struct lyd_node *node;
+ const char *dsc = "The <edit-config> operation loads all or part of a specified\n"
+ "configuration to the specified target configuration.";
+ const char *ref = "RFC 6241, Section 7.2";
+ const char *feats[] = {"writable-running", NULL};
+
+ assert_non_null((ly_ctx_load_module(UTEST_LYCTX, "ietf-netconf", "2011-06-01", feats)));
+
+ data = "<rpc message-id=\"25\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ "<edit-config>\n"
+ " <target>\n"
+ " <running/>\n"
+ " </target>\n"
+ " <config xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <l1 xmlns=\"urn:tests:a\" nc:operation=\"replace\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <c>val_c</c>\n"
+ " </l1>\n"
+ " <cp xmlns=\"urn:tests:a\">\n"
+ " <z nc:operation=\"delete\"/>\n"
+ " </cp>\n"
+ " </config>\n"
+ "</edit-config>\n"
+ "</rpc>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NETCONF_RPC, &tree, &op));
+ ly_in_free(in, 0);
+
+ assert_non_null(op);
+
+ node = tree;
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 1, 0, LY_PREF_XML, "rpc", 0, 0, 0, 0, "");
+
+ assert_non_null(tree);
+
+ node = op;
+ CHECK_LYSC_ACTION((struct lysc_node_action *)node->schema, dsc, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "edit-config", LYS_RPC,
+ 0, 0, 0, 0, 0, ref, 0);
+ node = lyd_child(node)->next;
+ dsc = "Inline Config content.";
+ CHECK_LYSC_NODE(node->schema, dsc, 0, LYS_STATUS_CURR | LYS_IS_INPUT, 1, "config", 0, LYS_ANYXML, 1, 0, NULL, 0);
+
+ node = ((struct lyd_node_any *)node)->value.tree;
+ CHECK_LYSC_NODE(node->schema, NULL, 0, LYS_CONFIG_W | LYS_STATUS_CURR | LYS_PRESENCE | LYS_SET_PRESENCE, 1, "cp",
+ 1, LYS_CONTAINER, 0, 0, NULL, 0);
+
+ node = lyd_child(node);
+ /* z has no value */
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0, LY_PREF_XML, "z", 0, 0, NULL, 0, "");
+ node = node->parent->next;
+ /* l1 key c has invalid value so it is at the end */
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)node, 0x1, 0x1, LY_PREF_XML, "l1", 0, 0, NULL, 0, "");
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\"/>\n");
+ CHECK_LYD_STRING(op, LYD_PRINT_WITHSIBLINGS,
+ "<edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <target>\n"
+ " <running/>\n"
+ " </target>\n"
+ " <config>\n"
+ " <cp xmlns=\"urn:tests:a\">\n"
+ " <z xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"delete\"/>\n"
+ " </cp>\n"
+ " <l1 xmlns=\"urn:tests:a\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\" nc:operation=\"replace\">\n"
+ " <a>val_a</a>\n"
+ " <b>val_b</b>\n"
+ " <c>val_c</c>\n"
+ " </l1>\n"
+ " </config>\n"
+ "</edit-config>\n");
+
+ lyd_free_all(tree);
+ lyd_free_all(op);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_netconf_action(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *tree, *op;
+
+ data = "<rpc message-id=\"25\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
+ "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">"
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <act>\n"
+ " <al>value</al>\n"
+ " </act>\n"
+ "</c>\n"
+ "</action>\n"
+ "</rpc>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_NETCONF_RPC, &tree, &op));
+ ly_in_free(in, 0);
+
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_PREF_XML, "rpc", 0, 0, 0, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_PREF_XML, "action", 0, 0, 0, 0, "");
+
+ assert_non_null(op);
+ CHECK_LYSC_ACTION((struct lysc_node_action *)op->schema, NULL, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "act", LYS_ACTION,
+ 1, 0, 0, 1, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"25\">\n"
+ " <action xmlns=\"urn:ietf:params:xml:ns:yang:1\"/>\n"
+ "</rpc>\n");
+ CHECK_LYD_STRING(op, LYD_PRINT_WITHSIBLINGS,
+ "<act xmlns=\"urn:tests:a\">\n"
+ " <al>value</al>\n"
+ "</act>\n");
+
+ lyd_free_all(tree);
+ lyd_free_all(op);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
+static void
+test_netconf_reply_or_notification(void **state)
+{
+ const char *data;
+ struct ly_in *in;
+ struct lyd_node *action, *tree, *op, *op2;
+
+ /* parse the action */
+ data = "<c xmlns=\"urn:tests:a\">\n"
+ " <act>\n"
+ " <al>value</al>\n"
+ " </act>\n"
+ "</c>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_YANG_RPC, &action, &op));
+ ly_in_free(in, 0);
+
+ /* parse notification first */
+ data = "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n"
+ "<eventTime>2010-12-06T08:00:01Z</eventTime>\n"
+ "<c xmlns=\"urn:tests:a\">\n"
+ " <n1>\n"
+ " <nl>value</nl>\n"
+ " </n1>\n"
+ "</c>\n"
+ "</notification>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_NETCONF_REPLY_OR_NOTIF, &tree, &op2));
+ ly_in_free(in, 0);
+
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 0, 1, LY_PREF_XML, "notification", 0, 0, 0, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_PREF_XML, "eventTime", 0, 0, 0, 0,
+ "2010-12-06T08:00:01Z");
+
+ assert_non_null(op2);
+ CHECK_LYSC_NOTIF((struct lysc_node_notif *)op2->schema, 1, NULL, 0, 0x4, 1, 0, "n1", 1, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\">\n"
+ " <eventTime>2010-12-06T08:00:01Z</eventTime>\n"
+ "</notification>\n");
+ CHECK_LYD_STRING(op2, LYD_PRINT_WITHSIBLINGS,
+ "<n1 xmlns=\"urn:tests:a\">\n"
+ " <nl>value</nl>\n"
+ "</n1>\n");
+
+ lyd_free_all(tree);
+ lyd_free_all(op2);
+
+ /* parse a data reply */
+ data = "<rpc-reply message-id=\"55\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
+ " <al xmlns=\"urn:tests:a\">25</al>\n"
+ "</rpc-reply>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_NETCONF_REPLY_OR_NOTIF, &tree, &op2));
+ ly_in_free(in, 0);
+
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 0, LY_PREF_XML, "rpc-reply", 0, 0, 0, 0, "");
+
+ assert_non_null(op2);
+ CHECK_LYSC_ACTION((struct lysc_node_action *)op2->schema, NULL, 0, LYS_STATUS_CURR,
+ 1, 0, 0, 1, "act", LYS_ACTION,
+ 1, 0, 0, 1, 0, NULL, 0);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS,
+ "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\"/>\n");
+ CHECK_LYD_STRING(op2, LYD_PRINT_WITHSIBLINGS,
+ "<act xmlns=\"urn:tests:a\">\n"
+ " <al>25</al>\n"
+ " <al>value</al>\n"
+ "</act>\n");
+
+ lyd_free_all(tree);
+ /* it was connected to the action, do not free */
+
+ /* parse an ok reply */
+ data = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\">\n"
+ " <ok/>\n"
+ "</rpc-reply>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_NETCONF_REPLY_OR_NOTIF, &tree, &op2));
+ ly_in_free(in, 0);
+
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_PREF_XML, "rpc-reply", 0, 0, 0, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 0, LY_PREF_XML, "ok", 0, 0, 0, 0, "");
+
+ assert_null(op2);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+
+ lyd_free_all(tree);
+
+ /* parse an error reply */
+ data = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"55\">\n"
+ " <rpc-error>\n"
+ " <error-type>rpc</error-type>\n"
+ " <error-tag>missing-attribute</error-tag>\n"
+ " <error-severity>error</error-severity>\n"
+ " <error-info>\n"
+ " <bad-attribute>message-id</bad-attribute>\n"
+ " <bad-element>rpc</bad-element>\n"
+ " </error-info>\n"
+ " </rpc-error>\n"
+ "</rpc-reply>\n";
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, op, in, LYD_XML, LYD_TYPE_NETCONF_REPLY_OR_NOTIF, &tree, &op2));
+ ly_in_free(in, 0);
+
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)tree, 1, 1, LY_PREF_XML, "rpc-reply", 0, 0, 0, 0, "");
+ CHECK_LYD_NODE_OPAQ((struct lyd_node_opaq *)lyd_child(tree), 0, 1, LY_PREF_XML, "rpc-error", 0, 0, 0, 0, "");
+
+ assert_null(op2);
+
+ CHECK_LYD_STRING(tree, LYD_PRINT_WITHSIBLINGS, data);
+
+ lyd_free_all(tree);
+
+ lyd_free_all(action);
+
+ /* wrong namespace, element name, whatever... */
+ /* TODO */
+}
+
int
main(void)
{
@@ -499,6 +753,9 @@
UTEST(test_action, setup),
UTEST(test_notification, setup),
UTEST(test_reply, setup),
+ UTEST(test_netconf_rpc, setup),
+ UTEST(test_netconf_action, setup),
+ UTEST(test_netconf_reply_or_notification, setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c
index cb0a306..3dbdf23 100644
--- a/tests/utests/data/test_validation.c
+++ b/tests/utests/data/test_validation.c
@@ -1132,11 +1132,11 @@
" </act>\n"
" </l1>\n"
"</cont>\n", &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_rpc(UTEST_LYCTX, in, LYD_XML, &op_tree, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_YANG_RPC, &op_tree, NULL));
assert_non_null(op_tree);
/* missing leafref */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_VALIDATE_OP_RPC, NULL));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_TYPE_YANG_RPC, NULL));
CHECK_LOG_CTX("Invalid leafref value \"target\" - no existing target instance \"/lf3\".",
"Schema location /j:cont/l1/act/input/lf2, data location /j:cont/l1[k='val1']/act/lf2.");
ly_in_free(in, 0);
@@ -1148,7 +1148,7 @@
LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
/* input must false */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC, NULL));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_TYPE_YANG_RPC, NULL));
CHECK_LOG_CTX("Must condition \"../../lf1 = 'true'\" not satisfied.",
"Data location /j:cont/l1[k='val1']/act.");
@@ -1160,7 +1160,7 @@
LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
/* success */
- assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_TYPE_YANG_RPC, NULL));
lyd_free_tree(op_tree);
lyd_free_siblings(tree);
@@ -1183,12 +1183,12 @@
" </act>\n"
" </l1>\n"
"</cont>\n", &in));
- assert_int_equal(LY_SUCCESS, lyd_parse_reply(UTEST_LYCTX, in, LYD_XML, &op_tree, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_parse_op(UTEST_LYCTX, NULL, in, LYD_XML, LYD_TYPE_YANG_REPLY, &op_tree, NULL));
assert_non_null(op_tree);
ly_in_free(in, 0);
/* missing leafref */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_VALIDATE_OP_REPLY, NULL));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, NULL, LYD_TYPE_YANG_REPLY, NULL));
CHECK_LOG_CTX("Invalid leafref value \"target\" - no existing target instance \"/lf4\".",
"Schema location /j:cont/l1/act/output/lf2, data location /j:cont/l1[k='val1']/act/lf2.");
@@ -1199,7 +1199,7 @@
LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
/* input must false */
- assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY, NULL));
+ assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_TYPE_YANG_REPLY, NULL));
CHECK_LOG_CTX("Must condition \"../../lf1 = 'true2'\" not satisfied.", "Data location /j:cont/l1[k='val1']/act.");
lyd_free_all(tree);
@@ -1210,7 +1210,7 @@
LYD_XML, LYD_PARSE_ONLY, 0, LY_SUCCESS, tree);
/* success */
- assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_TYPE_YANG_REPLY, NULL));
lyd_free_tree(op_tree);
lyd_free_all(tree);
diff --git a/tools/lint/cmd_data.c b/tools/lint/cmd_data.c
index b73d778..2029ff1 100644
--- a/tools/lint/cmd_data.c
+++ b/tools/lint/cmd_data.c
@@ -125,7 +125,7 @@
uint32_t options_print = 0;
uint32_t options_parse = YL_DEFAULT_DATA_PARSE_OPTIONS;
uint32_t options_validate = 0;
- uint8_t data_type = 0;
+ enum lyd_type data_type = 0;
uint8_t data_type_set = 0;
LYD_FORMAT outformat = LYD_UNKNOWN;
LYD_FORMAT informat = LYD_UNKNOWN;
@@ -232,11 +232,11 @@
} else if (!strcasecmp(optarg, "edit")) {
options_parse |= LYD_PARSE_ONLY;
} else if (!strcasecmp(optarg, "rpc") || !strcasecmp(optarg, "action")) {
- data_type = LYD_VALIDATE_OP_RPC;
+ data_type = LYD_TYPE_YANG_RPC;
} else if (!strcasecmp(optarg, "reply") || !strcasecmp(optarg, "rpcreply")) {
- data_type = LYD_VALIDATE_OP_REPLY;
+ data_type = LYD_TYPE_YANG_REPLY;
} else if (!strcasecmp(optarg, "notif") || !strcasecmp(optarg, "notification")) {
- data_type = LYD_VALIDATE_OP_NOTIF;
+ data_type = LYD_TYPE_YANG_NOTIF;
} else if (!strcasecmp(optarg, "data")) {
/* default option */
} else {
diff --git a/tools/lint/common.c b/tools/lint/common.c
index ed28665..db8c1f8 100644
--- a/tools/lint/common.c
+++ b/tools/lint/common.c
@@ -437,7 +437,7 @@
}
LY_ERR
-process_data(struct ly_ctx *ctx, uint8_t data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
+process_data(struct ly_ctx *ctx, enum lyd_type data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
uint32_t options_parse, uint32_t options_validate, uint32_t options_print,
struct cmdline_file *operational_f, struct ly_set *inputs, struct ly_set *xpaths)
{
@@ -447,7 +447,7 @@
/* additional operational datastore */
if (operational_f && operational_f->in) {
- ret = lyd_parse_data(ctx, operational_f->in, operational_f->format, LYD_PARSE_ONLY, 0, &operational);
+ ret = lyd_parse_data(ctx, NULL, operational_f->in, operational_f->format, LYD_PARSE_ONLY, 0, &operational);
if (ret) {
YLMSG_E("Failed to parse operational datastore file \"%s\".\n", operational_f->path);
goto cleanup;
@@ -457,17 +457,13 @@
for (uint32_t u = 0; u < inputs->count; ++u) {
struct cmdline_file *input_f = (struct cmdline_file *)inputs->objs[u];
switch (data_type) {
- case 0:
- ret = lyd_parse_data(ctx, input_f->in, input_f->format, options_parse, options_validate, &tree);
+ case LYD_TYPE_YANG_DATA:
+ ret = lyd_parse_data(ctx, NULL, input_f->in, input_f->format, options_parse, options_validate, &tree);
break;
- case LYD_VALIDATE_OP_RPC:
- ret = lyd_parse_rpc(ctx, input_f->in, input_f->format, &tree, NULL);
- break;
- case LYD_VALIDATE_OP_REPLY:
- ret = lyd_parse_reply(ctx, input_f->in, input_f->format, &tree, NULL);
- break;
- case LYD_VALIDATE_OP_NOTIF:
- ret = lyd_parse_notif(ctx, input_f->in, input_f->format, &tree, NULL);
+ case LYD_TYPE_YANG_RPC:
+ case LYD_TYPE_YANG_REPLY:
+ case LYD_TYPE_YANG_NOTIF:
+ ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, data_type, &tree, NULL);
break;
default:
YLMSG_E("Internal error (%s:%d).\n", __FILE__, __LINE__);
diff --git a/tools/lint/common.h b/tools/lint/common.h
index 85f7997..0a62db9 100644
--- a/tools/lint/common.h
+++ b/tools/lint/common.h
@@ -177,8 +177,7 @@
* @brief Process the input data files - parse, validate and print according to provided options.
*
* @param[in] ctx libyang context with schema.
- * @param[in] data_type The type of data in the input files, can be 0 for standard data tree and ::LYD_VALIDATE_OP values for
- * the operations.
+ * @param[in] data_type The type of data in the input files.
* @param[in] merge Flag if the data should be merged before validation.
* @param[in] format Data format for printing.
* @param[in] out The output handler for printing.
@@ -192,7 +191,7 @@
* is printed. Alternative to data printing.
* return LY_ERR value.
*/
-LY_ERR process_data(struct ly_ctx *ctx, uint8_t data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
+LY_ERR process_data(struct ly_ctx *ctx, enum lyd_type data_type, uint8_t merge, LYD_FORMAT format, struct ly_out *out,
uint32_t options_parse, uint32_t options_validate, uint32_t options_print,
struct cmdline_file *operational_f, struct ly_set *inputs, struct ly_set *xpaths);
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 7c794d6..e1cb12f 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -67,7 +67,7 @@
* data
*/
/* various options based on --type option */
- uint8_t data_type; /* values taken from LYD_VALIDATE_OP and extended by 0 for standard data tree */
+ enum lyd_type data_type;
uint32_t data_parse_options;
uint32_t data_validate_options;
uint32_t data_print_options;
@@ -538,11 +538,11 @@
} else if (!strcasecmp(optarg, "edit")) {
c->data_parse_options |= LYD_PARSE_ONLY;
} else if (!strcasecmp(optarg, "rpc") || !strcasecmp(optarg, "action")) {
- c->data_type = LYD_VALIDATE_OP_RPC;
+ c->data_type = LYD_TYPE_YANG_RPC;
} else if (!strcasecmp(optarg, "reply") || !strcasecmp(optarg, "rpcreply")) {
- c->data_type = LYD_VALIDATE_OP_REPLY;
+ c->data_type = LYD_TYPE_YANG_REPLY;
} else if (!strcasecmp(optarg, "notif") || !strcasecmp(optarg, "notification")) {
- c->data_type = LYD_VALIDATE_OP_NOTIF;
+ c->data_type = LYD_TYPE_YANG_NOTIF;
} else if (!strcasecmp(optarg, "data")) {
/* default option */
} else {