schema compile CHANGE support for leafref type
diff --git a/src/common.h b/src/common.h
index 63077ed..f90ae36 100644
--- a/src/common.h
+++ b/src/common.h
@@ -112,12 +112,12 @@
#define LY_CHECK_RET(...) GETMACRO2(__VA_ARGS__, LY_CHECK_RET2, LY_CHECK_RET1)(__VA_ARGS__)
#define LY_CHECK_ERR_RET(COND, ERR, RETVAL) if (COND) {ERR; return RETVAL;}
-#define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!ARG) {LOGARG(CTX, ARG);goto GOTO;}
+#define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!(ARG)) {LOGARG(CTX, ARG);goto GOTO;}
#define LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO) LY_CHECK_ARG_GOTO1(CTX, ARG1, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG2, GOTO)
#define LY_CHECK_ARG_GOTO3(CTX, ARG1, ARG2, ARG3, GOTO) LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG3, GOTO)
#define LY_CHECK_ARG_GOTO(CTX, ...) GETMACRO4(__VA_ARGS__, LY_CHECK_ARG_GOTO3, LY_CHECK_ARG_GOTO2, LY_CHECK_ARG_GOTO1)(CTX, __VA_ARGS__)
-#define LY_CHECK_ARG_RET1(CTX, ARG, RETVAL) if (!ARG) {LOGARG(CTX, ARG);return RETVAL;}
+#define LY_CHECK_ARG_RET1(CTX, ARG, RETVAL) if (!(ARG)) {LOGARG(CTX, ARG);return RETVAL;}
#define LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL) LY_CHECK_ARG_RET1(CTX, ARG1, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG2, RETVAL)
#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_RET(CTX, ...) GETMACRO4(__VA_ARGS__, LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1)(CTX, __VA_ARGS__)
diff --git a/src/tree_schema.c b/src/tree_schema.c
index cacadf7..9ababa3 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -30,6 +30,125 @@
#include "tree_schema_internal.h"
#include "xpath.h"
+API const struct lysc_node *
+lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, int options)
+{
+ const struct lysc_node *next;
+ struct lysc_node **snode;
+
+ LY_CHECK_ARG_RET(NULL, parent || module, NULL);
+
+ if (!last) {
+ /* first call */
+
+ /* get know where to start */
+ if (parent) {
+ /* schema subtree */
+ snode = lysc_node_children(parent);
+ /* do not return anything if the augment does not have any children */
+ if (!snode || !(*snode)) {
+ return NULL;
+ }
+ next = last = *snode;
+ } else {
+ /* top level data */
+ next = last = module->data;
+ }
+ if (!next) {
+ return next;
+ } else if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
+ if (!lys_is_disabled(next, 0)) {
+ return next;
+ }
+ /* continue to find next available node */
+ } else {
+ return next;
+ }
+ }
+
+ next = last->next;
+repeat:
+ if (!next) {
+ return next;
+ }
+ switch (next->nodetype) {
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ case LYS_LEAF:
+ case LYS_ANYXML:
+ case LYS_ANYDATA:
+ case LYS_LIST:
+ case LYS_LEAFLIST:
+ break;
+ case LYS_CONTAINER:
+ if (!(((struct lysc_node_container *)next)->flags & LYS_PRESENCE) && (options & LYS_GETNEXT_INTONPCONT)) {
+ if (((struct lysc_node_container *)next)->child) {
+ /* go into */
+ next = ((struct lysc_node_container *)next)->child;
+ } else {
+ next = next->next;
+ }
+ goto repeat;
+ }
+ break;
+ case LYS_CHOICE:
+ if (options & LYS_GETNEXT_WITHCHOICE) {
+ return next;
+ } else if (((struct lysc_node_choice *)next)->cases) {
+ /* go into */
+ next = ((struct lysc_node_choice *)next)->cases[0].child;
+ } else {
+ next = next->next;
+ }
+ goto repeat;
+ default:
+ /* we should not be here */
+ LOGINT(last->module->compiled->ctx);
+ return NULL;
+ }
+
+ if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
+ /* check if the node is disabled by if-feature */
+ if (lys_is_disabled(next, 0)) {
+ next = next->next;
+ goto repeat;
+ }
+ }
+ return next;
+}
+
+API const struct lysc_node *
+lys_child(const struct lysc_node *parent, const struct lys_module *module,
+ const char *name, size_t name_len, uint16_t nodetype, int options)
+{
+ const struct lysc_node *node = NULL;
+
+ LY_CHECK_ARG_RET(NULL, module, name, NULL);
+ if (!nodetype) {
+ nodetype = 0xffff;
+ }
+
+ while ((node = lys_getnext(node, parent, module->compiled, options))) {
+ if (!(node->nodetype & nodetype)) {
+ continue;
+ }
+ if (node->module != module) {
+ continue;
+ }
+
+ if (name_len) {
+ if (!strncmp(node->name, name, name_len) && !node->name[name_len]) {
+ return node;
+ }
+ } else {
+ if (!strcmp(node->name, name)) {
+ return node;
+ }
+ }
+ }
+ return NULL;
+}
+
API int
lysc_feature_value(const struct lysc_feature *feature)
{
@@ -282,6 +401,39 @@
return -1;
}
+API const struct lysc_iffeature *
+lys_is_disabled(const struct lysc_node *node, int recursive)
+{
+ unsigned int u;
+ struct lysc_iffeature **iff;
+
+ LY_CHECK_ARG_RET(NULL, node, NULL);
+
+ while(node) {
+ if (node->nodetype & LYS_CHOICE) {
+ return NULL;
+ }
+
+ iff = lysc_node_iff(node);
+ if (iff && *iff) {
+ /* check local if-features */
+ LY_ARRAY_FOR(*iff, u) {
+ if (!lysc_iffeature_value(&(*iff)[u])) {
+ return &(*iff)[u];
+ }
+ }
+ }
+
+ if (!recursive) {
+ return NULL;
+ }
+
+ /* go through parents */
+ node = node->parent;
+ }
+ return NULL;
+}
+
static void
lys_latest_switch(struct lys_module *old, struct lysp_module *new)
{
diff --git a/src/tree_schema.h b/src/tree_schema.h
index dab3b1b..61e04b6 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -156,10 +156,10 @@
#define LYS_LEAFLIST 0x0008 /**< leaf-list statement node */
#define LYS_LIST 0x0010 /**< list statement node */
#define LYS_ANYXML 0x0020 /**< anyxml statement node */
-#define LYS_CASE 0x0040 /**< case statement node */
-#define LYS_USES 0x0080 /**< uses statement node */
#define LYS_ANYDATA 0x0120 /**< anydata statement node, in tests it can be used for both #LYS_ANYXML and #LYS_ANYDATA */
+#define LYS_CASE 0x0040 /**< case statement node */
+#define LYS_USES 0x0080 /**< uses statement node */
#define LYS_INOUT 0x200
#define LYS_ACTION 0x400
#define LYS_NOTIF 0x800
@@ -940,6 +940,7 @@
*/
struct lysc_when {
struct lyxp_expr *cond; /**< XPath when condition */
+ struct lysc_node *context; /**< context node of the expression */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
};
@@ -1072,7 +1073,7 @@
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
LY_DATA_TYPE basetype; /**< Base type of the type */
uint32_t refcount; /**< reference counter for type sharing */
- struct lysc_node* target; /**< Target schema node */
+ const char* path; /**< target path */
uint8_t require_instance; /**< require-instance flag */
};
@@ -1158,6 +1159,14 @@
struct lysc_notif *notifs; /**< list of notifications ([sized array](@ref sizedarrays)) */
};
+struct lysc_node_case {
+ const char *name; /**< name of the case, including the implicit case */
+ struct lysc_node *child; /**< first child node of the case (linked list). Note that all the children of all the sibling cases are linked
+ each other as siblings with the parent pointer pointing to the choice node holding the case. To distinguish
+ which children node belongs to which case, it is needed to match the first children of the cases while going
+ through the children linked list. */
+};
+
struct lysc_node_choice {
uint16_t nodetype; /**< LYS_CHOICE */
uint16_t flags; /**< [schema node flags](@ref snodeflags) */
@@ -1172,7 +1181,10 @@
const char *name; /**< node name (mandatory) */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- struct lysc_node *child;
+ struct lysc_node_case *cases; /**< list of the cases with their name and pointer to the first children of each case ([sized array](@ref sizedarrays))
+ Note that all the children of all the cases are linked each other as siblings with the parent pointer pointing
+ to this choice node. To distinguish which children node belongs to which case, it is needed to match the first
+ children of the cases while going through the children linked list. */
};
struct lysc_node_leaf {
@@ -1247,40 +1259,6 @@
};
-struct lysc_node_case {
- uint16_t nodetype; /**< LYS_CASE */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) */
- struct lys_module *module; /**< module structure */
- struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
- struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
- struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
- struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
- never NULL. If there is no sibling node, pointer points to the node
- itself. In case of the first node, this pointer points to the last
- node in the list. */
- const char *name; /**< node name (mandatory) */
- struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-
- struct lysc_node *child;
-};
-
-struct lysc_node_uses {
- uint16_t nodetype; /**< LYS_CHOICE */
- uint16_t flags; /**< [schema node flags](@ref snodeflags) */
- struct lys_module *module; /**< module structure */
- struct lysp_node *sp; /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
- struct lysc_node *parent; /**< parent node (NULL in case of top level node) */
- struct lysc_node *next; /**< next sibling node (NULL if there is no one) */
- struct lysc_node *prev; /**< pointer to the previous sibling node \note Note that this pointer is
- never NULL. If there is no sibling node, pointer points to the node
- itself. In case of the first node, this pointer points to the last
- node in the list. */
- const char *name; /**< node name (mandatory) */
- struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-
- struct lysc_node *child;
-};
-
/**
* @brief Compiled YANG schema tree structure representing YANG module.
*
@@ -1438,10 +1416,7 @@
* @{
*/
#define LYSC_OPT_FREE_SP 1 /**< Free the input printable schema */
-
-/**
- * @}
- */
+/** @} scflags */
/**
* @brief Compile printable schema into a validated schema linking all the references.
@@ -1453,6 +1428,68 @@
*/
LY_ERR lys_compile(struct lys_module *mod, int options);
+/**
+ * @brief Get next schema tree (sibling) node element that can be instantiated in a data tree. Returned node can
+ * be from an augment.
+ *
+ * lys_getnext() is supposed to be called sequentially. In the first call, the \p last parameter is usually NULL
+ * and function starts returning i) the first \p parent's child or ii) the first top level element of the \p module.
+ * Consequent calls suppose to provide the previously returned node as the \p last parameter and still the same
+ * \p parent and \p module parameters.
+ *
+ * Without options, the function is used to traverse only the schema nodes that can be paired with corresponding
+ * data nodes in a data tree. By setting some \p options the behavior can be modified to the extent that
+ * all the schema nodes are iteratively returned.
+ *
+ * @param[in] last Previously returned schema tree node, or NULL in case of the first call.
+ * @param[in] parent Parent of the subtree where the function starts processing.
+ * @param[in] module In case of iterating on top level elements, the \p parent is NULL and
+ * module must be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Next schema tree node that can be instantiated in a data tree, NULL in case there is no such element.
+ */
+const struct lysc_node *lys_getnext(const struct lysc_node *last, const struct lysc_node *parent,
+ const struct lysc_module *module, int options);
+
+/**
+ * @defgroup sgetnextflags lys_getnext() flags
+ * @ingroup schematree
+ *
+ * @{
+ */
+#define LYS_GETNEXT_WITHCHOICE 0x01 /**< lys_getnext() option to allow returning #LYS_CHOICE nodes instead of looking into them */
+#define LYS_GETNEXT_INTONPCONT 0x40 /**< lys_getnext() option to look into non-presence container, instead of returning container itself */
+#define LYS_GETNEXT_NOSTATECHECK 0x100 /**< lys_getnext() option to skip checking module validity (import-only, disabled) and
+ relevant if-feature conditions state */
+/** @} sgetnextflags */
+
+/**
+ * @brief Get child node according to the specified criteria.
+ *
+ * @param[in] parent Optional parent of the node to find. If not specified, the module's top-level nodes are searched.
+ * @param[in] module module of the node to find. It is also limitation for the children node of the given parent.
+ * @param[in] name Name of the node to find.
+ * @param[in] name_len Optional length of the name in case it is not NULL-terminated string.
+ * @param[in] nodetype Optional criteria (to speedup) specifying nodetype(s) of the node to find.
+ * Used as a bitmask, so multiple nodetypes can be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Found node if any.
+ */
+const struct lysc_node *lys_child(const struct lysc_node *parent, const struct lys_module *module,
+ const char *name, size_t name_len, uint16_t nodetype, int options);
+
+/**
+ * @brief Check if the schema node is disabled in the schema tree, i.e. there is any disabled if-feature statement
+ * affecting the node.
+ *
+ * @param[in] node Schema node to check.
+ * @param[in] recursive - 0 to check if-feature only in the \p node schema node,
+ * - 1 to check if-feature in all ascendant schema nodes until there is a node possibly having an instance in a data tree
+ * @return - NULL if enabled,
+ * - pointer to the node with the unsatisfied (disabling) if-feature expression.
+ */
+const struct lysc_iffeature *lys_is_disabled(const struct lysc_node *node, int recursive);
+
/** @} */
#ifdef __cplusplus
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 63bc3c5..93fa536 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -493,6 +493,19 @@
return ret;
}
+/**
+ * @brief Find and process the referenced base identities from another identity or identityref
+ *
+ * For bases in identity se backlinks to them from the base identities. For identityref, store
+ * the array of pointers to the base identities. So one of the ident or bases parameter must be set
+ * to distinguish these two use cases.
+ *
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] bases_p Array of names (including prefix if necessary) of base identities.
+ * @param[in] ident Referencing identity to work with.
+ * @param[in] bases Array of bases of identityref to fill in.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_identity_bases(struct lysc_ctx *ctx, const char **bases_p, struct lysc_ident *ident, struct lysc_ident ***bases)
{
@@ -560,6 +573,13 @@
return LY_SUCCESS;
}
+/**
+ * @brief For the given array of identities, set the backlinks from all their base identities.
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] idents_p Array of identities definitions from the parsed schema structure.
+ * @param[in] idents Array of referencing identities to which the backlinks are supposed to be set.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
static LY_ERR
lys_compile_identities_derived(struct lysc_ctx *ctx, struct lysp_ident *idents_p, struct lysc_ident *idents)
{
@@ -574,6 +594,14 @@
return LY_SUCCESS;
}
+/**
+ * @brief Create compiled feature structure.
+ * @param[in] ctx Compile context.
+ * @param[in] feature_p Parsed feature definition to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] feature Compiled feature structure to fill.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_feature(struct lysc_ctx *ctx, struct lysp_feature *feature_p, int options, struct lysc_feature *feature)
{
@@ -602,6 +630,19 @@
return ret;
}
+/**
+ * @brief Validate and normalize numeric value from a range definition.
+ * @param[in] ctx Compile context.
+ * @param[in] basetype Base YANG built-in type of the node connected with the range restriction. Actually only LY_TYPE_DEC64 is important to
+ * allow processing of the fractions. The fraction point is extracted from the value which is then normalize according to given frdigits into
+ * valcopy to allow easy parsing and storing of the value. libyang stores decimal number without the decimal point which is always recovered from
+ * the known fraction-digits value. So, with fraction-digits 2, number 3.14 is stored as 314 and number 1 is stored as 100.
+ * @param[in] frdigits The fraction-digits of the type in case of LY_TYPE_DEC64.
+ * @param[in] value String value of the range boundary.
+ * @param[out] len Number of the processed bytes from the value. Processing stops on the first character which is not part of the number boundary.
+ * @param[out] valcopy NULL-terminated string with the numeric value to parse and store.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID (no number) or LY_EINVAL (decimal64 not matching fraction-digits value).
+ */
static LY_ERR
range_part_check_value_syntax(struct lysc_ctx *ctx, LY_DATA_TYPE basetype, uint8_t frdigits, const char *value, size_t *len, char **valcopy)
{
@@ -668,8 +709,15 @@
return LY_SUCCESS;
}
+/**
+ * @brief Check that values in range are in ascendant order.
+ * @param[in] unsigned_value Flag to note that we are working with unsigned values.
+ * @param[in] value Current value to check.
+ * @param[in] prev_value The last seen value.
+ * @return LY_SUCCESS or LY_EEXIST for invalid order.
+ */
static LY_ERR
-range_part_check_ascendance(int unsigned_value, int64_t value, int64_t prev_value)
+range_part_check_ascendancy(int unsigned_value, int64_t value, int64_t prev_value)
{
if (unsigned_value) {
if ((uint64_t)prev_value >= (uint64_t)value) {
@@ -683,6 +731,21 @@
return LY_SUCCESS;
}
+/**
+ * @brief Set min/max value of the range part.
+ * @param[in] ctx Compile context.
+ * @param[in] part Range part structure to fill.
+ * @param[in] max Flag to distinguish if storing min or max value.
+ * @param[in] prev The last seen value to check that all values in range are specified in ascendant order.
+ * @param[in] basetype Type of the value to get know implicit min/max values and other checking rules.
+ * @param[in] first Flag for the first value of the range to avoid ascendancy order.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in,out] value Numeric range value to be stored, if not provided the type's min/max value is set.
+ * @return LY_ERR value - LY_SUCCESS, LY_EDENIED (value brokes type's boundaries), LY_EVALID (not a number),
+ * LY_EEXIST (value is smaller than the previous one), LY_EINVAL (decimal64 value does not corresponds with the
+ * frdigits value), LY_EMEM.
+ */
static LY_ERR
range_part_minmax(struct lysc_ctx *ctx, struct lysc_range_part *part, int max, int64_t prev, LY_DATA_TYPE basetype, int first, int length_restr,
uint8_t frdigits, const char **value)
@@ -706,7 +769,7 @@
part->min_u64 = UINT64_C(0);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_DEC64: /* range */
@@ -719,7 +782,7 @@
part->min_64 = INT64_C(-9223372036854775807) - INT64_C(1);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_INT8: /* range */
@@ -731,7 +794,7 @@
part->min_64 = INT64_C(-128);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_INT16: /* range */
@@ -743,7 +806,7 @@
part->min_64 = INT64_C(-32768);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_INT32: /* range */
@@ -755,7 +818,7 @@
part->min_64 = INT64_C(-2147483648);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_INT64: /* range */
@@ -768,7 +831,7 @@
part->min_64 = INT64_C(-9223372036854775807) - INT64_C(1);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_UINT8: /* range */
@@ -780,7 +843,7 @@
part->min_u64 = UINT64_C(0);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_UINT16: /* range */
@@ -792,7 +855,7 @@
part->min_u64 = UINT64_C(0);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_UINT32: /* range */
@@ -804,7 +867,7 @@
part->min_u64 = UINT64_C(0);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_UINT64: /* range */
@@ -816,7 +879,7 @@
part->min_u64 = UINT64_C(0);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
}
break;
case LY_TYPE_STRING: /* length */
@@ -828,7 +891,7 @@
part->min_u64 = UINT64_C(0);
}
if (!ret && !first) {
- ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+ ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
}
break;
default:
@@ -857,6 +920,18 @@
return ret;
}
+/**
+ * @brief Compile the parsed range restriction.
+ * @param[in] ctx Compile context.
+ * @param[in] range_p Parsed range structure to compile.
+ * @param[in] basetype Base YANG built-in type of the node with the range restriction.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in] base_range Range restriction of the type from which the current type is derived. The current
+ * range restriction must be more restrictive than the base_range.
+ * @param[in,out] range Pointer to the created current range structure.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_type_range(struct lysc_ctx *ctx, struct lysp_restr *range_p, LY_DATA_TYPE basetype, int length_restr, uint8_t frdigits,
struct lysc_range *base_range, struct lysc_range **range)
@@ -1080,9 +1155,10 @@
/**
* @brief Checks pattern syntax.
*
+ * @param[in] ctx Compile context.
* @param[in] pattern Pattern to check.
- * @param[out] pcre_precomp Precompiled PCRE pattern. Can be NULL.
- * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
+ * @param[in,out] pcre_precomp Precompiled PCRE pattern. If NULL, the compiled information used to validate pattern are freed.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID.
*/
static LY_ERR
lys_compile_type_pattern_check(struct lysc_ctx *ctx, const char *pattern, pcre **pcre_precomp)
@@ -1287,6 +1363,16 @@
#undef URANGE_LEN
}
+/**
+ * @brief Compile parsed pattern restriction in conjunction with the patterns from base type.
+ * @param[in] ctx Compile context.
+ * @param[in] patterns_p Array of parsed patterns from the current type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] base_patterns Compiled patterns from the type from which the current type is derived.
+ * Patterns from the base type are inherited to have all the patterns that have to match at one place.
+ * @param[out] patterns Pointer to the storage for the patterns of the current type.
+ * @return LY_ERR LY_SUCCESS, LY_EMEM, LY_EVALID.
+ */
static LY_ERR
lys_compile_type_patterns(struct lysc_ctx *ctx, struct lysp_restr *patterns_p, int options,
struct lysc_pattern **base_patterns, struct lysc_pattern ***patterns)
@@ -1326,6 +1412,9 @@
return ret;
}
+/**
+ * @brief map of the possible restrictions combination for the specific built-in type.
+ */
static uint16_t type_substmt_map[LY_DATA_TYPE_COUNT] = {
0 /* LY_TYPE_UNKNOWN */,
LYS_SET_LENGTH /* LY_TYPE_BINARY */,
@@ -1349,6 +1438,16 @@
LYS_SET_RANGE /* LY_TYPE_UINT64 */
};
+/**
+ * @brief Compile parsed type's enum structures (for enumeration and bits types).
+ * @param[in] ctx Compile context.
+ * @param[in] enums_p Array of the parsed enum structures to compile.
+ * @param[in] basetype Base YANG built-in type from which the current type is derived. Only LY_TYPE_ENUM and LY_TYPE_BITS are expected.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] base_enums Array of the compiled enums information from the (latest) base type to check if the current enums are compatible.
+ * @param[out] enums Newly created array of the compiled enums information for the current type.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
static LY_ERR
lys_compile_type_enums(struct lysc_ctx *ctx, struct lysp_type_enum *enums_p, LY_DATA_TYPE basetype, int options,
struct lysc_type_enum_item *base_enums, struct lysc_type_enum_item **enums)
@@ -1484,6 +1583,384 @@
return ret;
}
+#define MOVE_PATH_PARENT(NODE, LIMIT_COND, TERM, ERR_MSG, ...) \
+ for ((NODE) = (NODE)->parent; \
+ (NODE) && !((NODE)->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_ACTION | LYS_NOTIF | LYS_ACTION)); \
+ (NODE) = (NODE)->parent); \
+ if (!(NODE) && (LIMIT_COND)) { /* we are going higher than top-level */ \
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, ERR_MSG, ##__VA_ARGS__); \
+ TERM; \
+ }
+
+/**
+ * @brief Validate the predicate(s) from the leafref path.
+ * @param[in] ctx Compile context
+ * @param[in, out] predicate Pointer to the predicate in the leafref path. The pointer is moved after the validated predicate(s).
+ * Since there can be multiple adjacent predicates for lists with multiple keys, all such predicates are validated.
+ * @param[in] context_node Predicate context node (where the predicate is placed).
+ * @param[in] startnode Path context node (where the leafref path begins/is placed).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_leafref_predicate_validate(struct lysc_ctx *ctx, const char **predicate,
+ const struct lysc_node *context_node, const struct lysc_node *startnode)
+{
+ LY_ERR ret = LY_EVALID;
+ const struct lys_module *mod;
+ const struct lysc_node *src_node, *dst_node;
+ const char *path_key_expr, *pke_start, *src, *src_prefix, *dst, *dst_prefix;
+ size_t src_len, src_prefix_len, dst_len, dst_prefix_len;
+ unsigned int dest_parent_times;
+ const char *start, *end, *pke_end;
+ struct ly_set keys = {0};
+ int i;
+
+ while (**predicate == '[') {
+ start = (*predicate)++;
+
+ while (isspace(**predicate)) {
+ ++(*predicate);
+ }
+ LY_CHECK_GOTO(lys_parse_nodeid(predicate, &src_prefix, &src_prefix_len, &src, &src_len), cleanup);
+ while (isspace(**predicate)) {
+ ++(*predicate);
+ }
+ if (**predicate != '=') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - missing \"=\".", *predicate - start + 1, *predicate);
+ goto cleanup;
+ }
+ ++(*predicate);
+ while (isspace(**predicate)) {
+ ++(*predicate);
+ }
+
+ if ((end = pke_end = strchr(*predicate, ']')) == NULL) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%s\" - missing predicate termination.", start);
+ goto cleanup;
+ }
+ --pke_end;
+ while (isspace(*pke_end)) {
+ --pke_end;
+ }
+ /* localize path-key-expr */
+ pke_start = path_key_expr = *predicate;
+ /* move after the current predicate */
+ *predicate = end + 1;
+
+ /* source (must be leaf or leaf-list) */
+ if (src_prefix) {
+ mod = lys_module_find_prefix(startnode->module, src_prefix, src_prefix_len);
+ } else {
+ mod = startnode->module;
+ }
+ src_node = lys_child(context_node, mod, src, src_len,
+ mod->compiled->version < LYS_VERSION_1_1 ? LYS_LEAF : LYS_LEAF | LYS_LEAFLIST, LYS_GETNEXT_NOSTATECHECK);
+ if (!src_node) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - key node \"%.*s\" from module \"%s\" not found.",
+ *predicate - start, start, src_len, src, mod->compiled->name);
+ goto cleanup;
+ }
+ /* TODO - check the src_node is really a key of the context_node */
+
+ /* check that there is only one predicate for the */
+ i = ly_set_add(&keys, (void*)src_node, 0);
+ LY_CHECK_GOTO(i == -1, cleanup);
+ if (keys.count != (unsigned int)i + 1) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - multiple equality test for the key %s.",
+ *predicate - start, start, src_node->name);
+ goto cleanup;
+ }
+
+ /* destination */
+ dest_parent_times = 0;
+ dst_node = context_node;
+
+ /* current-function-invocation *WSP "/" *WSP rel-path-keyexpr */
+ if (strncmp(path_key_expr, "current()", 9)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - missing current-function-invocation.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+ path_key_expr += 9;
+ while (isspace(*path_key_expr)) {
+ ++path_key_expr;
+ }
+
+ if (*path_key_expr != '/') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - missing \"/\" after current-function-invocation.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+ ++path_key_expr;
+ while (isspace(*path_key_expr)) {
+ ++path_key_expr;
+ }
+
+ /* rel-path-keyexpr:
+ * 1*(".." *WSP "/" *WSP) *(node-identifier *WSP "/" *WSP) node-identifier */
+ while (!strncmp(path_key_expr, "..", 2)) {
+ ++dest_parent_times;
+ path_key_expr += 2;
+ while (isspace(*path_key_expr)) {
+ ++path_key_expr;
+ }
+ if (*path_key_expr != '/') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - missing \"/\" in \"../\" rel-path-keyexpr pattern.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+ ++path_key_expr;
+ while (isspace(*path_key_expr)) {
+ ++path_key_expr;
+ }
+
+ /* path is supposed to be evaluated in data tree, so we have to skip
+ * all schema nodes that cannot be instantiated in data tree */
+ MOVE_PATH_PARENT(dst_node, !strncmp(path_key_expr, "..", 2), goto cleanup,
+ "Invalid leafref path predicate \"%.*s\" - too many \"..\" in rel-path-keyexpr.",
+ *predicate - start, start);
+ }
+ if (!dest_parent_times) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - at least one \"..\" is expected in rel-path-keyexpr.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+ if (path_key_expr == pke_end) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - at least one node-identifier is expected in rel-path-keyexpr.",
+ *predicate - start, start);
+ goto cleanup;
+ }
+
+ while(path_key_expr != pke_end) {
+ if (lys_parse_nodeid(&path_key_expr, &dst_prefix, &dst_prefix_len, &dst, &dst_len)) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid node identifier in leafref path predicate - character %d (%.*s).",
+ path_key_expr - start, *predicate - start, start);
+ goto cleanup;
+ }
+
+ if (dst_prefix) {
+ mod = lys_module_find_prefix(startnode->module, dst_prefix, dst_prefix_len);
+ } else {
+ mod = startnode->module;
+ }
+ if (!mod) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - unable to find module of the node \"%.*s\" in rel-path_keyexpr.",
+ *predicate - start, start, dst_len, dst);
+ goto cleanup;
+ }
+
+ dst_node = lys_child(dst_node, mod, dst, dst_len, 0, LYS_GETNEXT_NOSTATECHECK);
+ if (!dst_node) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - unable to find node \"%.*s\" in the rel-path_keyexpr.",
+ *predicate - start, start, path_key_expr - pke_start, pke_start);
+ goto cleanup;
+ }
+ }
+ if (!(dst_node->nodetype & (dst_node->module->compiled->version < LYS_VERSION_1_1 ? LYS_LEAF : LYS_LEAF | LYS_LEAFLIST))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path predicate \"%.*s\" - rel-path_keyexpr \"%.*s\" refers %s.",
+ *predicate - start, start, path_key_expr - pke_start, pke_start, lys_nodetype2str(dst_node->nodetype));
+ goto cleanup;
+ }
+ }
+
+ ret = LY_SUCCESS;
+cleanup:
+ ly_set_erase(&keys, NULL);
+ return ret;
+}
+
+/**
+ * @brief Parse path-arg (leafref). Get tokens of the path by repetitive calls of the function.
+ *
+ * path-arg = absolute-path / relative-path
+ * absolute-path = 1*("/" (node-identifier *path-predicate))
+ * relative-path = 1*(".." "/") descendant-path
+ *
+ * @param[in,out] path Path to parse.
+ * @param[out] prefix Prefix of the token, NULL if there is not any.
+ * @param[out] pref_len Length of the prefix, 0 if there is not any.
+ * @param[out] name Name of the token.
+ * @param[out] nam_len Length of the name.
+ * @param[out] parent_times Number of leading ".." in the path. Must be 0 on the first call,
+ * must not be changed between consecutive calls. -1 if the
+ * path is absolute.
+ * @param[out] has_predicate Flag to mark whether there is a predicate specified.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the path.
+ */
+static LY_ERR
+lys_path_token(const char **path, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len,
+ int *parent_times, int *has_predicate)
+{
+ int par_times = 0;
+
+ assert(path && *path);
+ assert(parent_times);
+ assert(prefix);
+ assert(prefix_len);
+ assert(name);
+ assert(name_len);
+ assert(has_predicate);
+
+ *prefix = NULL;
+ *prefix_len = 0;
+ *name = NULL;
+ *name_len = 0;
+ *has_predicate = 0;
+
+ if (!*parent_times) {
+ if (!strncmp(*path, "..", 2)) {
+ *path += 2;
+ ++par_times;
+ while (!strncmp(*path, "/..", 3)) {
+ *path += 3;
+ ++par_times;
+ }
+ }
+ if (par_times) {
+ *parent_times = par_times;
+ } else {
+ *parent_times = -1;
+ }
+ }
+
+ if (**path != '/') {
+ return LY_EINVAL;
+ }
+ /* skip '/' */
+ ++(*path);
+
+ /* node-identifier ([prefix:]name) */
+ LY_CHECK_RET(lys_parse_nodeid(path, prefix, prefix_len, name, name_len));
+
+ if ((**path == '/' && (*path)[1]) || !**path) {
+ /* path continues by another token or this is the last token */
+ return LY_SUCCESS;
+ } else if ((*path)[0] != '[') {
+ /* unexpected character */
+ return LY_EINVAL;
+ } else {
+ /* predicate starting with [ */
+ *has_predicate = 1;
+ return LY_SUCCESS;
+ }
+}
+
+/**
+ * @brief Validate the leafref path.
+ * @param[in] ctx Compile context
+ * @param[in] startnode Path context node (where the leafref path begins/is placed).
+ * @param[in] path Leafref path to validate.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_leafref_validate(struct lysc_ctx *ctx, struct lysc_node *startnode, const char *path)
+{
+ const struct lysc_node *node = NULL, *parent = NULL;
+ const struct lys_module *mod;
+ const char *id, *prefix, *name;
+ size_t prefix_len, name_len;
+ int parent_times = 0, has_predicate;
+ unsigned int iter, u;
+ LY_ERR ret = LY_SUCCESS;
+
+ assert(ctx);
+ assert(startnode);
+ assert(path);
+
+ iter = 0;
+ id = path;
+ while(*id && (ret = lys_path_token(&id, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate)) == LY_SUCCESS) {
+ if (!iter) { /* first iteration */
+ /* precess ".." in relative paths */
+ if (parent_times > 0) {
+ /* move from the context node */
+ for (u = 0, parent = startnode; u < (unsigned int)parent_times; u++) {
+ /* path is supposed to be evaluated in data tree, so we have to skip
+ * all schema nodes that cannot be instantiated in data tree */
+ MOVE_PATH_PARENT(parent, u < (unsigned int)parent_times - 1, return LY_EVALID,
+ "Invalid leafref path \"%s\" - too many \"..\" in the path.", path);
+ }
+ }
+ }
+
+ if (prefix) {
+ mod = lys_module_find_prefix(startnode->module, prefix, prefix_len);
+ } else {
+ mod = startnode->module;
+ }
+ if (!mod) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path - unable to find module connected with the prefix of the node \"%.*s\".", id - path, path);
+ return LY_EVALID;
+ }
+
+ node = lys_child(parent, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+ if (!node) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path - unable to find \"%.*s\".", id - path, path);
+ return LY_EVALID;
+ }
+ parent = node;
+
+ if (has_predicate) {
+ /* we have predicate, so the current result must be list */
+ if (node->nodetype != LYS_LIST) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path - node \"%.*s\" is expected to be a list, but it is %s.",
+ id - path, path, lys_nodetype2str(node->nodetype));
+ return LY_EVALID;
+ }
+
+ LY_CHECK_RET(lys_compile_leafref_predicate_validate(ctx, &id, node, startnode), LY_EVALID);
+ }
+
+ ++iter;
+ }
+ if (ret) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+ "Invalid leafref path at character %d (%s).", id - path + 1, path);
+ return LY_EVALID;
+ }
+
+ if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid leafref path \"%s\" - target node is %s instead of leaf or leaf-list.",
+ path, lys_nodetype2str(node->nodetype));
+ return LY_EVALID;
+ }
+
+ /* check status */
+ if (lysc_check_status(ctx, startnode->flags, startnode->module, startnode->name, node->flags, node->module, node->name)) {
+ return LY_EVALID;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief The core of the lys_compile_type() - compile information about the given type (from typedef or leaf/leaf-list).
+ * @param[in] ctx Compile context.
+ * @param[in] type_p Parsed type to compile.
+ * @param[in] basetype Base YANG built-in type of the type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] tpdfname Name of the type's typedef, serves as a flag - if it is leaf/leaf-list's type, it is NULL.
+ * @param[in] base The latest base (compiled) type from which the current type is being derived.
+ * @param[out] type Newly created type structure with the filled information about the type.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_type_(struct lysc_ctx *ctx, struct lysp_type *type_p, LY_DATA_TYPE basetype, int options, const char *tpdfname,
struct lysc_type *base, struct lysc_type **type)
@@ -1715,6 +2192,32 @@
*type = calloc(1, sizeof(struct lysc_type_identityref));
}
break;
+ case LY_TYPE_LEAFREF:
+ /* RFC 7950 9.9.3 - require-instance */
+ if (type_p->flags & LYS_SET_REQINST) {
+ ((struct lysc_type_leafref*)(*type))->require_instance = type_p->require_instance;
+ } else {
+ /* default is true */
+ ((struct lysc_type_leafref*)(*type))->require_instance = 1;
+ }
+ if (type_p->path) {
+ DUP_STRING(ctx->ctx, (void*)type_p->path, ((struct lysc_type_leafref*)(*type))->path);
+ } else if (base) {
+ DUP_STRING(ctx->ctx, ((struct lysc_type_leafref*)base)->path, ((struct lysc_type_leafref*)(*type))->path);
+ } else if (tpdfname) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "path", "leafref type ", tpdfname);
+ return LY_EVALID;
+ } else {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "path", "leafref type", "");
+ free(*type);
+ *type = NULL;
+ return LY_EVALID;
+ }
+ if (tpdfname) {
+ type_p->compiled = *type;
+ *type = calloc(1, sizeof(struct lysc_type_leafref));
+ }
+ break;
case LY_TYPE_INST:
/* RFC 7950 9.9.3 - require-instance */
if (type_p->flags & LYS_SET_REQINST) {
@@ -1739,6 +2242,14 @@
return ret;
}
+/**
+ * @brief Compile information about the leaf/leaf-list's type.
+ * @param[in] ctx Compile context.
+ * @param[in] leaf_p Parsed leaf with the type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[out] type Newly created (or reused with increased refcount) type structure with the filled information about the type.
+ * @return LY_ERR value.
+ */
static LY_ERR
lys_compile_type(struct lysc_ctx *ctx, struct lysp_node_leaf *leaf_p, int options, struct lysc_type **type)
{
@@ -1860,7 +2371,7 @@
} else if (tctx->tpdf->type.compiled) {
base = tctx->tpdf->type.compiled;
continue;
- } else if ((u != tpdf_chain.count - 1) && !(tctx->tpdf->type.flags)) {
+ } else if ((basetype != LY_TYPE_LEAFREF) && (u != tpdf_chain.count - 1) && !(tctx->tpdf->type.flags)) {
/* no change, just use the type information from the base */
base = ((struct lysp_tpdf*)tctx->tpdf)->type.compiled = ((struct type_context*)tpdf_chain.objs[u + 1])->tpdf->type.compiled;
++base->refcount;
@@ -1876,8 +2387,8 @@
}
/* process the type definition in leaf */
- if (leaf_p->type.flags || !base) {
- /* get restrictions from the node itself, finalize the type structure */
+ if (leaf_p->type.flags || !base || basetype == LY_TYPE_LEAFREF) {
+ /* get restrictions from the node itself */
(*type)->basetype = basetype;
++(*type)->refcount;
ret = lys_compile_type_(ctx, &leaf_p->type, basetype, options, NULL, base, type);
@@ -1898,6 +2409,15 @@
static LY_ERR lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent);
+/**
+ * @brief Compile parsed container node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed container node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the container-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
static LY_ERR
lys_compile_node_container(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
{
@@ -1922,6 +2442,15 @@
return ret;
}
+/**
+ * @brief Compile parsed leaf node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed leaf node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the leaf-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
static LY_ERR
lys_compile_node_leaf(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
{
@@ -1937,12 +2466,27 @@
ret = lys_compile_type(ctx, leaf_p, options, &leaf->type);
LY_CHECK_GOTO(ret, done);
+ if (leaf->type->basetype == LY_TYPE_LEAFREF) {
+ /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+ ly_set_add(&ctx->unres, leaf, 0);
+ }
+
DUP_STRING(ctx->ctx, leaf_p->units, leaf->units);
DUP_STRING(ctx->ctx, leaf_p->dflt, leaf->dflt);
done:
return ret;
}
+/**
+ * @brief Compile parsed schema node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed schema node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] parent Compiled parent node where the current node is supposed to be connected. It is
+ * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
+ * the compile context.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
static LY_ERR
lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent)
{
@@ -1966,15 +2510,9 @@
case LYS_LEAFLIST:
node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_leaflist));
break;
- case LYS_CASE:
- node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_case));
- break;
case LYS_CHOICE:
node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_choice));
break;
- case LYS_USES:
- node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_uses));
- break;
case LYS_ANYXML:
case LYS_ANYDATA:
node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_anydata));
@@ -2035,15 +2573,19 @@
LY_CHECK_GOTO(node_compile_spec(ctx, node_p, options, node), error);
/* insert into parent's children */
- if (parent && (children = lysc_node_children(parent))) {
- if (!(*children)) {
- /* first child */
- *children = node;
- } else {
- /* insert at the end of the parent's children list */
- (*children)->prev->next = node;
- node->prev = (*children)->prev;
- (*children)->prev = node;
+ if (parent) {
+ if (parent->nodetype == LYS_CHOICE) {
+ /* TODO exception for cases */
+ } else if ((children = lysc_node_children(parent))) {
+ if (!(*children)) {
+ /* first child */
+ *children = node;
+ } else {
+ /* insert at the end of the parent's children list */
+ (*children)->prev->next = node;
+ node->prev = (*children)->prev;
+ (*children)->prev = node;
+ }
}
} else {
/* top-level element */
@@ -2064,11 +2606,18 @@
return ret;
}
+/**
+ * @brief Compile the given YANG module.
+ * @param[in] mod Module structure where the parsed schema is expected and the compiled schema will be placed.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
LY_ERR
lys_compile(struct lys_module *mod, int options)
{
struct lysc_ctx ctx = {0};
struct lysc_module *mod_c;
+ struct lysc_type *type;
struct lysp_module *sp;
struct lysp_node *node_p;
unsigned int u;
@@ -2114,6 +2663,20 @@
COMPILE_ARRAY_GOTO(&ctx, sp->exts, mod_c->exts, options, u, lys_compile_ext, ret, error);
+ /* validate leafref's paths and when/must xpaths */
+ for (u = 0; u < ctx.unres.count; ++u) {
+ if (((struct lysc_node*)ctx.unres.objs[u])->nodetype == LYS_LEAF) {
+ type = ((struct lysc_node_leaf*)ctx.unres.objs[u])->type;
+ if (type->basetype == LY_TYPE_LEAFREF) {
+ /* validate the path */
+ ret = lys_compile_leafref_validate(&ctx, ((struct lysc_node*)ctx.unres.objs[u]),
+ ((struct lysc_type_leafref*)type)->path);
+ LY_CHECK_GOTO(ret, error);
+ }
+ }
+ }
+ ly_set_erase(&ctx.unres, NULL);
+
if (options & LYSC_OPT_FREE_SP) {
lysp_module_free(mod->parsed);
((struct lys_module*)mod)->parsed = NULL;
@@ -2123,6 +2686,7 @@
return LY_SUCCESS;
error:
+ ly_set_erase(&ctx.unres, NULL);
lysc_module_free(mod_c, NULL);
((struct lys_module*)mod)->compiled = NULL;
return ret;
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 7e74603..1e52dd8 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -516,6 +516,12 @@
FREE_ARRAY(ctx, item->exts, lysc_ext_instance_free);
}
+static void lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type);
+static void
+lysc_type2_free(struct ly_ctx *ctx, struct lysc_type **type)
+{
+ lysc_type_free(ctx, *type);
+}
static void
lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type)
{
@@ -552,6 +558,12 @@
case LY_TYPE_IDENT:
LY_ARRAY_FREE(((struct lysc_type_identityref*)type)->bases);
break;
+ case LY_TYPE_UNION:
+ FREE_ARRAY(ctx, ((struct lysc_type_union*)type)->types, lysc_type2_free);
+ break;
+ case LY_TYPE_LEAFREF:
+ FREE_STRING(ctx, ((struct lysc_type_leafref*)type)->path);
+ break;
case LY_TYPE_INST:
case LY_TYPE_BOOL:
case LY_TYPE_EMPTY:
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 52b6f61..6f3815c 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -27,6 +27,63 @@
#include "libyang.h"
#include "tree_schema_internal.h"
+/**
+ * @brief Parse an identifier.
+ *
+ * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
+ * identifier = (ALPHA / "_")
+ * *(ALPHA / DIGIT / "_" / "-" / ".")
+ *
+ * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
+ */
+static LY_ERR
+lys_parse_id(const char **id)
+{
+ assert(id && *id);
+
+ if (!isalpha(**id) && (**id != '_')) {
+ return LY_EINVAL;
+ }
+ ++(*id);
+
+ while (isalnum(**id) || (**id == '_') || (**id == '-') || (**id == '.')) {
+ ++(*id);
+ }
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lys_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
+{
+ assert(id && *id);
+ assert(prefix && prefix_len);
+ assert(name && name_len);
+
+ *prefix = *id;
+ *prefix_len = 0;
+ *name = NULL;
+ *name_len = 0;
+
+ LY_CHECK_RET(lys_parse_id(id));
+ if (**id == ':') {
+ /* there is prefix */
+ *prefix_len = *id - *prefix;
+ ++(*id);
+ *name = *id;
+
+ LY_CHECK_RET(lys_parse_id(id));
+ *name_len = *id - *name;
+ } else {
+ /* there is no prefix, so what we have as prefix now is actually the name */
+ *name = *prefix;
+ *name_len = *id - *name;
+ *prefix = NULL;
+ }
+
+ return LY_SUCCESS;
+}
+
LY_ERR
lysp_check_prefix(struct ly_parser_ctx *ctx, struct lysp_module *module, const char **value)
{
@@ -737,7 +794,7 @@
}
struct lysc_module *
-lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len)
+lysc_module_find_prefix(const struct lysc_module *mod, const char *prefix, size_t len)
{
const struct lys_module *m = NULL;
@@ -746,7 +803,7 @@
}
struct lysp_module *
-lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len)
+lysp_module_find_prefix(const struct lysp_module *mod, const char *prefix, size_t len)
{
const struct lys_module *m = NULL;
@@ -755,7 +812,7 @@
}
struct lys_module *
-lys_module_find_prefix(struct lys_module *mod, const char *prefix, size_t len)
+lys_module_find_prefix(const struct lys_module *mod, const char *prefix, size_t len)
{
const struct lys_module *m = NULL;
@@ -767,6 +824,29 @@
return (struct lys_module*)m;
}
+const char *
+lys_nodetype2str(uint16_t nodetype)
+{
+ switch(nodetype) {
+ case LYS_CONTAINER:
+ return "container";
+ case LYS_CHOICE:
+ return "choice";
+ case LYS_LEAF:
+ return "leaf";
+ case LYS_LEAFLIST:
+ return "leaf-list";
+ case LYS_LIST:
+ return "list";
+ case LYS_ANYXML:
+ return "anyxml";
+ case LYS_ANYDATA:
+ return "anydata";
+ default:
+ return "unknown";
+ }
+}
+
struct lysp_tpdf **
lysp_node_typedefs(struct lysp_node *node)
{
@@ -851,20 +931,20 @@
}
struct lysc_node **
-lysc_node_children(struct lysc_node *node)
+lysc_node_children(const struct lysc_node *node)
{
assert(node);
switch (node->nodetype) {
case LYS_CONTAINER:
return &((struct lysc_node_container*)node)->child;
case LYS_CHOICE:
- return &((struct lysc_node_choice*)node)->child;
+ if (((struct lysc_node_choice*)node)->cases) {
+ return &((struct lysc_node_choice*)node)->cases[0].child;
+ } else {
+ return NULL;
+ }
case LYS_LIST:
return &((struct lysc_node_list*)node)->child;
- case LYS_CASE:
- return &((struct lysc_node_case*)node)->child;
- case LYS_USES:
- return &((struct lysc_node_uses*)node)->child;
/* TODO
case LYS_INOUT:
return &((struct lysc_action_inout*)node)->child;
@@ -876,3 +956,27 @@
}
}
+struct lysc_iffeature **
+lysc_node_iff(const struct lysc_node *node)
+{
+ assert(node);
+ switch (node->nodetype) {
+ case LYS_CONTAINER:
+ return &((struct lysc_node_container*)node)->iffeatures;
+ case LYS_LEAF:
+ return &((struct lysc_node_leaf*)node)->iffeatures;
+/* TODO
+ case LYS_LIST:
+ return &((struct lysc_node_list*)node)->iffeatures;
+ case LYS_CASE:
+ return &((struct lysc_node_case*)node)->iffeatures;
+ case LYS_USES:
+ return &((struct lysc_node_uses*)node)->iffeatures;
+ case LYS_NOTIF:
+ return &((struct lysc_notif*)node)->child;
+*/
+ default:
+ return NULL;
+ }
+}
+
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 1ca6d4c..cb7e2a4 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -56,6 +56,7 @@
struct lysc_ctx {
struct ly_ctx *ctx;
struct lys_module *mod;
+ struct ly_set unres; /* to validate leafref's target and xpath of when/must */
uint16_t path_len;
#define LYSC_CTX_BUFSIZE 4078
char path[LYSC_CTX_BUFSIZE];
@@ -178,7 +179,14 @@
* @param[in] node Node to check.
* @return Address of the node's child member if any, NULL otherwise.
*/
-struct lysc_node **lysc_node_children(struct lysc_node *node);
+struct lysc_node **lysc_node_children(const struct lysc_node *node);
+
+/**
+ * @brief Get address of a node's iffeatures pointer if any.
+ * @param[in] node Node to check.
+ * @return Address of the node's iffeature member if any, NULL otherwise.
+ */
+struct lysc_iffeature **lysc_node_iff(const struct lysc_node *node);
/**
* @brief Find the module referenced by prefix in the provided parsed mod.
@@ -188,7 +196,7 @@
* @param[in] len Length of the prefix since it is not necessary NULL-terminated.
* @return Pointer to the module or NULL if the module is not found.
*/
-struct lysp_module *lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len);
+struct lysp_module *lysp_module_find_prefix(const struct lysp_module *mod, const char *prefix, size_t len);
/**
* @brief Find the module referenced by prefix in the provided compiled mod.
@@ -198,7 +206,7 @@
* @param[in] len Length of the prefix since it is not necessary NULL-terminated.
* @return Pointer to the module or NULL if the module is not found.
*/
-struct lysc_module *lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len);
+struct lysc_module *lysc_module_find_prefix(const struct lysc_module *mod, const char *prefix, size_t len);
/**
* @brief Check statement's status for invalid combination.
@@ -221,6 +229,20 @@
uint16_t flags2, void *mod2, const char *name2);
/**
+ * @brief Parse a node-identifier.
+ *
+ * node-identifier = [prefix ":"] identifier
+ *
+ * @param[in, out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @param[out] prefix Node's prefix, NULL if there is not any.
+ * @param[out] prefix_len Length of the node's prefix, 0 if there is not any.
+ * @param[out] name Node's name.
+ * @param[out] nam_len Length of the node's name.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the id.
+ */
+LY_ERR lys_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len);
+
+/**
* @brief Find the module referenced by prefix in the provided mod.
*
* @param[in] mod Schema module where the prefix was used.
@@ -228,7 +250,14 @@
* @param[in] len Length of the prefix since it is not necessary NULL-terminated.
* @return Pointer to the module or NULL if the module is not found.
*/
-struct lys_module *lys_module_find_prefix(struct lys_module *mod, const char *prefix, size_t len);
+struct lys_module *lys_module_find_prefix(const struct lys_module *mod, const char *prefix, size_t len);
+
+/**
+ * @brief Stringify schema nodetype.
+ * @param[in] nodetype Nodetype to stringify.
+ * @return Constant string with the name of the node's type.
+ */
+const char *lys_nodetype2str(uint16_t nodetype);
/**
* @brief Parse YANG module and submodule from a string.
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index e3970c5..4e0d973 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -1211,6 +1211,133 @@
ly_ctx_destroy(ctx, NULL);
}
+static void
+test_type_leafref(void **state)
+{
+ *state = test_type_leafref;
+
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lysc_type *type;
+ const char *path, *name, *prefix;
+ size_t prefix_len, name_len;
+ int parent_times, has_predicate;
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+ /* lys_path_token() */
+ path = "invalid_path";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ path = "..";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ path = "..[";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ path = "../";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ path = "/";
+ parent_times = 0;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+
+ path = "../../pref:id/xxx[predicate]/invalid!!!";
+ parent_times = 0;
+ assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_string_equal("/xxx[predicate]/invalid!!!", path);
+ assert_int_equal(4, prefix_len);
+ assert_int_equal(0, strncmp("pref", prefix, prefix_len));
+ assert_int_equal(2, name_len);
+ assert_int_equal(0, strncmp("id", name, name_len));
+ assert_int_equal(2, parent_times);
+ assert_int_equal(0, has_predicate);
+ assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_string_equal("[predicate]/invalid!!!", path);
+ assert_int_equal(0, prefix_len);
+ assert_null(prefix);
+ assert_int_equal(3, name_len);
+ assert_int_equal(0, strncmp("xxx", name, name_len));
+ assert_int_equal(1, has_predicate);
+ path += 11;
+ assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_string_equal("!!!", path);
+ assert_int_equal(0, prefix_len);
+ assert_null(prefix);
+ assert_int_equal(7, name_len);
+ assert_int_equal(0, strncmp("invalid", name, name_len));
+
+ path = "/absolute/prefix:path";
+ parent_times = 0;
+ assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_string_equal("/prefix:path", path);
+ assert_int_equal(0, prefix_len);
+ assert_null(prefix);
+ assert_int_equal(8, name_len);
+ assert_int_equal(0, strncmp("absolute", name, name_len));
+ assert_int_equal(-1, parent_times);
+ assert_int_equal(0, has_predicate);
+ assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+ assert_int_equal(0, *path);
+ assert_int_equal(6, prefix_len);
+ assert_int_equal(0, strncmp("prefix", prefix, prefix_len));
+ assert_int_equal(4, name_len);
+ assert_int_equal(0, strncmp("path", name, name_len));
+ assert_int_equal(0, has_predicate);
+
+ /* complete leafref paths */
+ assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1;namespace urn:a;prefix a;"
+ "leaf ref1 {type leafref {path /a:target1;}} leaf ref2 {type leafref {path /a/target2; require-instance false;}}"
+ "leaf target1 {type string;}container a {leaf target2 {type uint8;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+ type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/a:target1", ((struct lysc_type_leafref*)type)->path);
+ assert_int_equal(1, ((struct lysc_type_leafref*)type)->require_instance);
+ type = ((struct lysc_node_leaf*)mod->compiled->data->next)->type;
+ assert_non_null(type);
+ assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+ assert_string_equal("/a/target2", ((struct lysc_type_leafref*)type)->path);
+ assert_int_equal(0, ((struct lysc_type_leafref*)type)->require_instance);
+
+ /* TODO target in list with predicates */
+
+
+ /* invalid paths */
+ assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path ../a/invalid;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path - unable to find \"../a/invalid\".");
+ assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path ../../toohigh;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path \"../../toohigh\" - too many \"..\" in the path.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path /a:invalid;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path - unable to find module connected with the prefix of the node \"/a:invalid\".");
+ assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd;leaf target1 {type string;}container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path '/a[target2 = current()/../target1]/target2';}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path - node \"/a\" is expected to be a list, but it is container.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path /a!invalid;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path at character 3 (/a!invalid).");
+ assert_non_null(mod = lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;container a {leaf target2 {type uint8;}}"
+ "leaf ref1 {type leafref {path /a;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("Invalid leafref path \"/a\" - target node is container instead of leaf or leaf-list.");
+ assert_non_null(mod = lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;container a {leaf target2 {type uint8; status deprecated;}}"
+ "leaf ref1 {type leafref {path /a/target2;}}}", LYS_IN_YANG));
+ assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+ logbuf_assert("A current definition \"ref1\" is not allowed to reference deprecated definition \"target2\".");
+
+ *state = NULL;
+ ly_ctx_destroy(ctx, NULL);
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
@@ -1225,6 +1352,7 @@
cmocka_unit_test_setup_teardown(test_type_dec64, logger_setup, logger_teardown),
cmocka_unit_test_setup_teardown(test_type_instanceid, logger_setup, logger_teardown),
cmocka_unit_test_setup_teardown(test_type_identityref, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_type_leafref, logger_setup, logger_teardown),
cmocka_unit_test_setup_teardown(test_node_container, logger_setup, logger_teardown),
};
diff --git a/tests/src/test_tree_schema_helpers.c b/tests/src/test_tree_schema_helpers.c
index 29b7e8c..1c5ca3d 100644
--- a/tests/src/test_tree_schema_helpers.c
+++ b/tests/src/test_tree_schema_helpers.c
@@ -66,6 +66,18 @@
return 0;
}
+static int
+logger_teardown(void **state)
+{
+ (void) state; /* unused */
+#if ENABLE_LOGGER_CHECKING
+ if (*state) {
+ fprintf(stderr, "%s\n", logbuf);
+ }
+#endif
+ return 0;
+}
+
void
logbuf_clean(void)
{
@@ -81,7 +93,7 @@
static void
test_date(void **state)
{
- (void) state; /* unused */
+ *state = test_date;
assert_int_equal(LY_EINVAL, lysp_check_date(NULL, NULL, 0, "date"));
logbuf_assert("Invalid argument date (lysp_check_date()).");
@@ -105,6 +117,8 @@
assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-11-11", 10, "date"));
assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-02-28", 10, "date"));
assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2016-02-29", 10, "date"));
+
+ *state = NULL;
}
static void
@@ -149,7 +163,7 @@
static void
test_typedef(void **state)
{
- (void) state; /* unused */
+ *state = test_typedef;
struct ly_ctx *ctx = NULL;
const char *str;
@@ -252,15 +266,48 @@
assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
logbuf_assert("Invalid name \"x\" of typedef - scoped type collide with a top-level type.");
+ *state = NULL;
ly_ctx_destroy(ctx, NULL);
}
+static void
+test_parse_nodeid(void **state)
+{
+ (void) state; /* unused */
+ const char *str;
+ const char *prefix, *name;
+ size_t prefix_len, name_len;
+
+ str = "123";
+ assert_int_equal(LY_EINVAL, lys_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+
+ str = "a12_-.!";
+ assert_int_equal(LY_SUCCESS, lys_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+ assert_null(prefix);
+ assert_int_equal(0, prefix_len);
+ assert_non_null(name);
+ assert_int_equal(6, name_len);
+ assert_int_equal(0, strncmp("a12_-.", name, name_len));
+ assert_string_equal("!", str);
+
+ str = "a12_-.:_b2 xxx";
+ assert_int_equal(LY_SUCCESS, lys_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+ assert_non_null(prefix);
+ assert_int_equal(6, prefix_len);
+ assert_int_equal(0, strncmp("a12_-.", prefix, prefix_len));
+ assert_non_null(name);
+ assert_int_equal(3, name_len);
+ assert_int_equal(0, strncmp("_b2", name, name_len));
+ assert_string_equal(" xxx", str);
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
- cmocka_unit_test_setup(test_date, logger_setup),
+ cmocka_unit_test_setup_teardown(test_date, logger_setup, logger_teardown),
cmocka_unit_test_setup(test_revisions, logger_setup),
- cmocka_unit_test_setup(test_typedef, logger_setup),
+ cmocka_unit_test_setup_teardown(test_typedef, logger_setup, logger_teardown),
+ cmocka_unit_test_setup(test_parse_nodeid, logger_setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);