printer_tree BUGFIX united tree for '@' nodes
When mounting a module, parent-references will show nodes in one tree
if they have a common parents.
diff --git a/src/printer_tree.c b/src/printer_tree.c
index 9e997cd..7fb42b4 100644
--- a/src/printer_tree.c
+++ b/src/printer_tree.c
@@ -71,6 +71,13 @@
* also split one node into multiple lines if the node does not fit
* on one line.
*
+ * @subsubsection TRP_trocm trocm
+ * Functions for Obtaining information from mounted shared schema (RFC 8528).
+ * Parent-references can be used to specify which nodes are accessible from the mounted module.
+ * These accessible nodes are printed including their parent nodes.
+ * Functions marked as 'trocm' operate on the Compiled schema tree.
+ *
+ *
* @subsubsection TRP_trt trt
* Data type marking in the printer_tree module.
*
@@ -3421,6 +3428,194 @@
}
/**********************************************************************
+ * Definition of trocm reading functions
+ *********************************************************************/
+
+/**
+ * @brief Get child node from current @p node.
+ * @param[in] node from which it tries to get a child.
+ * If set to NULL, then the top-level node of the @p parent_ref is returned.
+ * @param[in] parent_ref is one of the referenced nodes.
+ * @return Child of @p node, top-level node of @p parent_ref or NULL.
+ */
+static const struct lysc_node *
+trocm_node_child(const struct lysc_node *node, const struct lysc_node *parent_ref)
+{
+ const struct lysc_node *child, *parent;
+
+ assert(node != parent_ref);
+
+ child = parent_ref;
+ for (parent = parent_ref->parent; parent; parent = parent->parent) {
+ if (parent == node) {
+ return child;
+ }
+ child = parent;
+ }
+
+ if (!node) {
+ return child;
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * @brief Get first parent-referenced node from @p cmod.
+ * @param[in] parent_refs is the set of all parent-referenced nodes.
+ * @param[in] cmod is compiled module from which to get the first parent-ref node.
+ * @param[out] first_parent_ref is index to @p parent_refs where first parent-ref node is located.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+trocm_first_parent_ref(const struct ly_set *parent_refs, const struct lysc_module *cmod, uint32_t *first_parent_ref)
+{
+ uint32_t i;
+ const struct lysc_node *ref;
+
+ for (i = 0; i < parent_refs->count; i++) {
+ ref = parent_refs->snodes[i];
+ if (ref->module->compiled == cmod) {
+ *first_parent_ref = i;
+ return LY_SUCCESS;
+ }
+ }
+
+ return LY_ENOTFOUND;
+}
+
+/**
+ * @brief Get next parent-referenced node from @p cmod.
+ * @param[in] parent_refs is the set of all parent-referenced nodes.
+ * @param[in] cmod is compiled module from which to get the next parent-ref node.
+ * @param[in,out] parent_ref is index to @p parent_refs where the next parent-ref node is located.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+trocm_next_parent_ref(const struct ly_set *parent_refs, const struct lysc_module *cmod, uint32_t *parent_ref)
+{
+ (*parent_ref)++;
+ if (*parent_ref >= parent_refs->count) {
+ return LY_ENOT;
+ } else if (parent_refs->snodes[*parent_ref]->module->compiled != cmod) {
+ return LY_ENOT;
+ } else {
+ return LY_SUCCESS;
+ }
+}
+
+/**
+ * @brief Get next sibling of current node @p cn.
+ * @param[in] cn is current compiled node.
+ * @param[in] parent_refs is the set of all parent-referenced nodes.
+ * @return Next sibling or NULL.
+ */
+static const struct lysc_node *
+trocm_next_sibling(const struct lysc_node *cn, const struct ly_set *parent_refs)
+{
+ LY_ERR ret;
+ uint32_t i;
+ const struct lysc_node *sibl, *ref, *child;
+ const struct lysc_module *cmod;
+
+ cmod = cn->module->compiled;
+ for (sibl = cn->next; sibl; sibl = sibl->next) {
+ for (ret = trocm_first_parent_ref(parent_refs, cmod, &i);
+ ret == LY_SUCCESS;
+ ret = trocm_next_parent_ref(parent_refs, cmod, &i)) {
+ ref = parent_refs->snodes[i];
+ if (ref == sibl) {
+ /* Sibling is in the parent-refs. */
+ return sibl;
+ }
+ child = trocm_node_child(sibl, parent_refs->snodes[i]);
+ if (child) {
+ /* Return parent of parent-ref node. */
+ return sibl;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @copydoc trop_read_if_sibling_exists
+ */
+static ly_bool
+trocm_read_if_sibling_exists(const struct trt_tree_ctx *tc)
+{
+ if (trocm_next_sibling(tc->cn, tc->parent_refs)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**********************************************************************
+ * Modify trocm getters
+ *********************************************************************/
+
+/**
+ * @copydoc trop_modi_next_child()
+ */
+static struct trt_node
+trocm_modi_next_child(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ LY_ERR ret;
+ uint32_t i;
+ const struct lysc_node *child, *ref;
+
+ child = NULL;
+ for (ret = trocm_first_parent_ref(tc->parent_refs, tc->cmod, &i);
+ ret == LY_SUCCESS;
+ ret = trocm_next_parent_ref(tc->parent_refs, tc->cmod, &i)) {
+ ref = tc->parent_refs->snodes[i];
+ if (ref == tc->cn) {
+ continue;
+ }
+ if ((child = trocm_node_child(tc->cn, ref))) {
+ tc->cn = child;
+ return troc_read_node(ca, tc);
+ }
+ }
+
+ return TRP_EMPTY_NODE;
+}
+
+/**
+ * @copydoc ::trop_modi_first_sibling()
+ */
+static void
+trocm_modi_first_sibling(struct trt_tree_ctx *tc)
+{
+ uint32_t i;
+
+ if (troc_modi_parent(tc)) {
+ trocm_modi_next_child(TRP_EMPTY_PARENT_CACHE, tc);
+ } else {
+ trocm_first_parent_ref(tc->parent_refs, tc->cmod, &i);
+ tc->cn = trocm_node_child(NULL, tc->parent_refs->snodes[i]);
+ }
+}
+
+/**
+ * @copydoc ::trop_modi_next_sibling()
+ */
+static struct trt_node
+trocm_modi_next_sibling(struct trt_parent_cache ca, struct trt_tree_ctx *tc)
+{
+ const struct lysc_node *sibl;
+
+ if ((sibl = trocm_next_sibling(tc->cn, tc->parent_refs))) {
+ tc->cn = sibl;
+ return troc_read_node(ca, tc);
+ } else {
+ return TRP_EMPTY_NODE;
+ }
+}
+
+/**********************************************************************
* Definition of tree browsing functions
*********************************************************************/
@@ -4538,6 +4733,118 @@
return erc;
}
+/**********************************************************************
+ * Functions for YANG Schema mount.
+ *********************************************************************/
+
+/**
+ * @brief Callback data for lysc_module_dfs_full.
+ */
+struct sort_parent_refs_state{
+ struct ly_set *refs; /**< Set of parent-references pointers to sort. */
+ uint64_t glob; /**< Current index in sort_parent_refs_state.refs. */
+ uint64_t loc; /**< Current index of parent-ref node which belongs to the same module. */
+ uint64_t total; /**< Total number of parent-ref nodes which belongs to the same module. */
+};
+
+/**
+ * @brief Callback for lysc_module_dfs_full() which sorts parent-references.
+ * @param[in] node is current compiled node to check.
+ * @param[in,out] data are expected to be of type struct sort_parent_refs_state.
+ * @param[in] dfs_continue is not used.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+troc_dfs_clb(struct lysc_node *node, void *data, ly_bool *UNUSED(dfs_continue))
+{
+ struct sort_parent_refs_state *dfs_data;
+ struct lysc_node **snodes, *tmp;
+ uint64_t i;
+
+ dfs_data = data;
+ snodes = dfs_data->refs->snodes;
+ for (i = dfs_data->glob; i < dfs_data->refs->count; i++) {
+ if (snodes[i] == node) {
+ /* swap */
+ tmp = snodes[dfs_data->glob];
+ snodes[dfs_data->glob] = snodes[i];
+ snodes[i] = tmp;
+ /* increment counters */
+ dfs_data->glob++;
+ dfs_data->loc++;
+ break;
+ }
+ }
+
+ if (dfs_data->loc == dfs_data->total) {
+ /* Stop searching in the current module. */
+ return LY_ENOT;
+ } else {
+ return LY_SUCCESS;
+ }
+}
+
+/**
+ * @brief Sort parent-references so that the order matches deep-search-first.
+ * @param[in,out] refs is set of parent-references to sort.
+ */
+static void
+troc_sort_parent_refs(struct ly_set *refs)
+{
+ uint64_t i, j, same_mod;
+ const struct lys_module *mod;
+ struct sort_parent_refs_state dfs_data;
+
+ if (!refs || (refs->count == 0) || (refs->count == 1)) {
+ return;
+ }
+
+ dfs_data.refs = refs;
+ for (i = 0; i < refs->count; i++) {
+ mod = refs->snodes[i]->module;
+ /* Count total number of parent-references which refers to the same module. */
+ same_mod = 1;
+ for (j = i + 1; j < refs->count; j++) {
+ if (mod == refs->snodes[j]->module) {
+ ++same_mod;
+ }
+ }
+ if (same_mod == 1) {
+ continue;
+ }
+
+ /* Sort all parent-references in the current module. */
+ dfs_data.glob = i;
+ dfs_data.loc = 1;
+ dfs_data.total = same_mod;
+ lysc_module_dfs_full(mod, troc_dfs_clb, &dfs_data);
+ i = same_mod - 1;
+ }
+}
+
+/**
+ * @brief For next module get the first parent-reference.
+ * @param[in] parent_refs is set of parent-referenced nodes.
+ * @param[in,out] parent_ref is the index in @p parent_refs, which is set to next parent-reference.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+trocm_next_first_parent_ref(const struct ly_set *parent_refs, uint32_t *parent_ref)
+{
+ uint64_t i;
+ const struct lysc_module *cmod;
+
+ cmod = parent_refs->snodes[*parent_ref]->module->compiled;
+ for (i = (*parent_ref + 1); i < parent_refs->count; i++) {
+ if (cmod != parent_refs->snodes[i]->module->compiled) {
+ *parent_ref = i;
+ return LY_SUCCESS;
+ }
+ }
+
+ return LY_ENOT;
+}
+
/**
* @brief Print all mounted nodes ('/') and parent-referenced nodes ('@').
*
@@ -4549,7 +4856,7 @@
static LY_ERR
trb_print_mount_point(const struct lysc_ext_instance *ext, const struct trt_wrapper wr, struct trt_printer_ctx *pc)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR ret = LY_SUCCESS, rc;
struct ly_ctx *ext_ctx = NULL;
const struct lys_module *mod, *last_mod;
struct trt_tree_ctx tmptc;
@@ -4608,10 +4915,23 @@
}
/* Print parent-referenced nodes which are denoted by the symbol '@'. */
- for (i = 0; refs && i < refs->count; i++) {
+ if (!refs || (refs->count == 0)) {
+ goto cleanup;
+ }
+ troc_sort_parent_refs(refs);
+ rc = LY_SUCCESS;
+ /* Iterate over all modules which are in refs. */
+ for (i = 0; rc == LY_SUCCESS; rc = trocm_next_first_parent_ref(refs, &i)) {
trm_lysc_tree_ctx(refs->snodes[i]->module, pc->out, pc->max_line_length, 1, refs, &tmppc, &tmptc);
- tmpwr = ((i + 1) == refs->count) ? wr : trp_wrapper_set_mark_top(wr);
- trb_print_parents(refs->snodes[i], &tmpwr, pc, &tmptc);
+ tmptc.cn = trocm_node_child(NULL, refs->snodes[i]);
+ tmppc.fp.modify.first_sibling = trocm_modi_first_sibling;
+ tmppc.fp.modify.next_sibling = trocm_modi_next_sibling;
+ tmppc.fp.modify.next_child = trocm_modi_next_child;
+ tmppc.fp.read.if_sibling_exists = trocm_read_if_sibling_exists;
+ iter_state = i;
+ tmpwr = trocm_next_first_parent_ref(refs, &i) ? wr : trp_wrapper_set_mark_top(wr);
+ i = iter_state;
+ trb_print_family_tree(tmpwr, &tmppc, &tmptc);
}
cleanup:
diff --git a/tests/utests/schema/test_printer_tree.c b/tests/utests/schema/test_printer_tree.c
index 149af6e..922072d 100644
--- a/tests/utests/schema/test_printer_tree.c
+++ b/tests/utests/schema/test_printer_tree.c
@@ -2188,6 +2188,68 @@
assert_string_equal(printed, expect);
ly_out_reset(UTEST_OUT);
+ /*
+ * parent-ref composes the '@' subtree
+ */
+ orig = SM_MOD_MAIN("a37",
+ "container pr {\n"
+ " leaf ignored_node {\n"
+ " type string;\n"
+ " }\n"
+ " container cont {\n"
+ " leaf ignored_lf {\n"
+ " type uint32;\n"
+ " }\n"
+ " }\n"
+ " container ignored_subtree {\n"
+ " leaf ignored_lf {\n"
+ " type uint32;\n"
+ " }\n"
+ " }\n"
+ " container cont_sibl {\n"
+ " leaf slf {\n"
+ " type string;\n"
+ " }\n"
+ " }\n"
+ " leaf lf {\n"
+ " type uint32;\n"
+ " }\n"
+ "}\n"
+ "container cont_mount {\n"
+ " yangmnt:mount-point \"mnt-root\";\n"
+ "}\n");
+ expect =
+ "module: a37\n"
+ " +--rw pr\n"
+ " | +--rw ignored_node? string\n"
+ " | +--rw cont\n"
+ " | | +--rw ignored_lf? uint32\n"
+ " | +--rw ignored_subtree\n"
+ " | | +--rw ignored_lf? uint32\n"
+ " | +--rw cont_sibl\n"
+ " | | +--rw slf? string\n"
+ " | +--rw lf? uint32\n"
+ " +--mp cont_mount\n"
+ " +--rw tlist/ [name]\n"
+ " | +--rw name uint32\n"
+ " +--rw tcont/\n"
+ " | +--rw tleaf? uint32\n"
+ " +--rw pr@\n"
+ " +--rw cont\n"
+ " +--rw cont_sibl\n"
+ " | +--rw slf? string\n"
+ " +--rw lf? uint32\n";
+ data = EXT_DATA("a37", "", SCHEMA_REF_SHARED(
+ "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":cont_sibl/slf</parent-reference>\n"
+ "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":cont</parent-reference>\n"
+ "<parent-reference>/"SM_PREF ":pr/"SM_PREF ":lf</parent-reference>\n"));
+ ly_ctx_set_ext_data_clb(UTEST_LYCTX, getter, data);
+ UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
+ TEST_LOCAL_PRINT(mod, 72);
+ assert_int_equal(strlen(expect), ly_out_printed(UTEST_OUT));
+ assert_string_equal(printed, expect);
+ ly_out_reset(UTEST_OUT);
+
ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED);
TEST_LOCAL_TEARDOWN;
}