blob: c93bbc238d694ffcc875d45b9869b98befe4055e [file] [log] [blame]
Michal Vasko5ed10f12015-06-04 10:04:57 +02001/**
2 * @file printer/tree.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief TREE printer for libyang data model structure
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci54f6fb32016-02-24 12:56:39 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vasko8de098c2016-02-26 10:00:25 +010011 *
Radek Krejci54f6fb32016-02-24 12:56:39 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko5ed10f12015-06-04 10:04:57 +020013 */
14
Michal Vasko03d8cd22018-02-08 14:48:28 +010015#include <stdint.h>
Michal Vasko5ed10f12015-06-04 10:04:57 +020016#include <stdlib.h>
17#include <stdio.h>
18#include <string.h>
Michal Vaskob5c75d72015-06-15 12:16:52 +020019#include <assert.h>
Michal Vasko5ed10f12015-06-04 10:04:57 +020020
Radek Krejci998a0b82015-08-17 13:14:36 +020021#include "common.h"
Radek Krejci76b07902015-10-09 09:11:25 +020022#include "printer.h"
Michal Vasko2d162e12015-09-24 14:33:29 +020023#include "tree_schema.h"
Michal Vasko5ed10f12015-06-04 10:04:57 +020024
Michal Vasko568b1952018-01-30 15:53:30 +010025/* module: <name>
26 * <X>+--rw <node-name> */
27#define LY_TREE_MOD_DATA_INDENT 2
Michal Vasko5ed10f12015-06-04 10:04:57 +020028
Michal Vasko568b1952018-01-30 15:53:30 +010029/* <^>rpcs:
30 * <X>+---x <rpc-name> */
31#define LY_TREE_OP_DATA_INDENT 4
32
33/* +--rw leaf<X>string */
34#define LY_TREE_TYPE_INDENT 3
35
36/* +--rw leaf
37 * | <X>string */
38#define LY_TREE_WRAP_INDENT 2
39
40/* these options are mostly inherited in recursive print, non-recursive options are parameters */
41typedef struct {
42 const struct lys_module *module; /**< (sub)module we are printing from */
43 uint8_t base_indent; /**< base indent size of all the printed text */
44 uint64_t indent; /**< bit-field of sibling (1)/ no sibling(0) on corresponding depths */
45 uint16_t line_length; /**< maximum desired line length */
46 int spec_config; /**< special config flags - 0 (no special config status),
47 1 (read-only - rpc output, notification), 2 (write-only - rpc input) */
48 int options; /**< user-specified tree printer options */
49} tp_opts;
50
51static void tree_print_snode(struct lyout *out, int level, uint16_t max_name_len, const struct lys_node *node, int mask,
52 const struct lys_node *aug_parent, int subtree, tp_opts *opts);
53
54static int
55tree_print_indent(struct lyout *out, int level, tp_opts *opts)
Michal Vaskoe8441232016-09-01 15:32:45 +020056{
Michal Vasko568b1952018-01-30 15:53:30 +010057 int i, ret = 0;
Michal Vaskoe8441232016-09-01 15:32:45 +020058
Michal Vasko568b1952018-01-30 15:53:30 +010059 if (opts->base_indent) {
60 ret += ly_print(out, "%*s", opts->base_indent, " ");
61 }
62 for (i = 0; i < level; ++i) {
63 if (opts->indent & (1 << i)) {
64 ret += ly_print(out, "| ");
Michal Vaskoe8441232016-09-01 15:32:45 +020065 } else {
Michal Vasko568b1952018-01-30 15:53:30 +010066 ret += ly_print(out, " ");
Michal Vaskoe8441232016-09-01 15:32:45 +020067 }
68 }
Michal Vasko568b1952018-01-30 15:53:30 +010069
70 return ret;
Michal Vaskoe8441232016-09-01 15:32:45 +020071}
72
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020073static int
Michal Vasko568b1952018-01-30 15:53:30 +010074tree_sibling_is_valid_child(const struct lys_node *node, int including, const struct lys_module *module,
75 const struct lys_node *aug_parent, LYS_NODE nodetype)
Michal Vasko6db4fce2015-06-08 14:13:49 +020076{
Michal Vasko58416c82017-02-01 11:43:18 +010077 struct lys_node *cur, *cur2;
Michal Vasko7ea0a312015-06-08 10:36:48 +020078
Michal Vasko80630802017-02-15 13:55:57 +010079 assert(!aug_parent || (aug_parent->nodetype == LYS_AUGMENT));
80
Michal Vaskobda37192016-02-15 11:09:13 +010081 if (!node) {
Michal Vasko07898f92015-06-15 12:17:11 +020082 return 0;
Michal Vaskob3d6d192018-11-05 10:02:10 +010083 } else if (!lys_parent(node) && !strcmp(node->name, "config") && !strcmp(node->module->name, "ietf-netconf")) {
84 /* node added by libyang, not actually in the model */
85 return 0;
Michal Vasko07898f92015-06-15 12:17:11 +020086 }
Michal Vasko7ea0a312015-06-08 10:36:48 +020087
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020088 /* has a following printed child */
Radek Krejci1d82ef62015-08-07 14:44:40 +020089 LY_TREE_FOR((struct lys_node *)(including ? node : node->next), cur) {
Michal Vasko80630802017-02-15 13:55:57 +010090 if (aug_parent && (cur->parent != aug_parent)) {
91 /* we are done traversing this augment, the nodes are all direct siblings */
92 return 0;
93 }
94
Michal Vasko568b1952018-01-30 15:53:30 +010095 if (module->type && (lys_main_module(module) != lys_node_module(cur))) {
Michal Vaskobda37192016-02-15 11:09:13 +010096 continue;
97 }
98
Michal Vaskof6f18f42015-09-24 13:06:14 +020099 if (!lys_is_disabled(cur, 0)) {
Michal Vaskoe0f11be2018-03-02 14:08:22 +0100100 if ((cur->nodetype == LYS_USES) || ((cur->nodetype == LYS_CASE) && (cur->flags & LYS_IMPLICIT))) {
Michal Vasko568b1952018-01-30 15:53:30 +0100101 if (tree_sibling_is_valid_child(cur->child, 1, module, NULL, nodetype)) {
Michal Vaskoc1e89352017-01-25 14:57:55 +0100102 return 1;
103 }
Michal Vasko32113302017-02-01 11:34:22 +0100104 } else {
105 switch (nodetype) {
106 case LYS_GROUPING:
Michal Vaskoe0f11be2018-03-02 14:08:22 +0100107 /* we are printing groupings, they are printed separately */
Michal Vasko32113302017-02-01 11:34:22 +0100108 if (cur->nodetype == LYS_GROUPING) {
Michal Vasko568b1952018-01-30 15:53:30 +0100109 return 0;
Michal Vasko32113302017-02-01 11:34:22 +0100110 }
111 break;
112 case LYS_RPC:
113 if (cur->nodetype == LYS_RPC) {
114 return 1;
115 }
116 break;
117 case LYS_NOTIF:
118 if (cur->nodetype == LYS_NOTIF) {
119 return 1;
120 }
121 break;
122 default:
123 if (cur->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_CHOICE
Michal Vasko58416c82017-02-01 11:43:18 +0100124 | LYS_CASE | LYS_ACTION)) {
Michal Vasko32113302017-02-01 11:34:22 +0100125 return 1;
126 }
127 if ((cur->nodetype & (LYS_INPUT | LYS_OUTPUT)) && cur->child) {
128 return 1;
129 }
Michal Vasko58416c82017-02-01 11:43:18 +0100130 /* only nested notifications count here (not top-level) */
131 if (cur->nodetype == LYS_NOTIF) {
132 for (cur2 = lys_parent(cur); cur2 && (cur2->nodetype == LYS_USES); cur2 = lys_parent(cur2));
133 if (cur2) {
134 return 1;
135 }
136 }
Michal Vasko32113302017-02-01 11:34:22 +0100137 break;
Michal Vaskoc1e89352017-01-25 14:57:55 +0100138 }
Michal Vaskof6f18f42015-09-24 13:06:14 +0200139 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200140 }
141 }
Michal Vasko7ea0a312015-06-08 10:36:48 +0200142
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200143 /* if in uses, the following printed child can actually be in the parent node :-/ */
Michal Vaskodcf98e62016-05-05 17:53:53 +0200144 if (lys_parent(node) && (lys_parent(node)->nodetype == LYS_USES)) {
Michal Vasko568b1952018-01-30 15:53:30 +0100145 return tree_sibling_is_valid_child(lys_parent(node), 0, module, NULL, nodetype);
Michal Vasko07898f92015-06-15 12:17:11 +0200146 }
147
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200148 return 0;
Michal Vasko7ea0a312015-06-08 10:36:48 +0200149}
150
Michal Vasko568b1952018-01-30 15:53:30 +0100151static void
152tree_next_indent(int level, const struct lys_node *node, const struct lys_node *aug_parent, tp_opts *opts)
Michal Vasko449afde2015-06-04 16:06:49 +0200153{
Michal Vaskodc300b02017-04-07 14:09:20 +0200154 int next_is_case = 0, has_next = 0;
Michal Vasko14410462015-06-05 15:08:54 +0200155
Michal Vaskoe8441232016-09-01 15:32:45 +0200156 if (level > 64) {
Michal Vasko53b7da02018-02-13 15:28:42 +0100157 LOGINT(node->module->ctx);
Michal Vasko568b1952018-01-30 15:53:30 +0100158 return;
Michal Vasko253035f2015-12-17 16:58:13 +0100159 }
160
Michal Vasko568b1952018-01-30 15:53:30 +0100161 /* clear level indent (it may have been set for some line wrapping) */
Michal Vasko03d8cd22018-02-08 14:48:28 +0100162 opts->indent &= ~(uint64_t)(1ULL << (level - 1));
Michal Vasko14410462015-06-05 15:08:54 +0200163
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200164 /* this is the direct child of a case */
Michal Vaskodc300b02017-04-07 14:09:20 +0200165 if ((node->nodetype != LYS_CASE) && lys_parent(node) && (lys_parent(node)->nodetype & (LYS_CASE | LYS_CHOICE))) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200166 /* it is not the only child */
Michal Vaskodcf98e62016-05-05 17:53:53 +0200167 if (node->next && lys_parent(node->next) && (lys_parent(node->next)->nodetype == LYS_CHOICE)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200168 next_is_case = 1;
169 }
170 }
Michal Vasko14410462015-06-05 15:08:54 +0200171
Michal Vasko07898f92015-06-15 12:17:11 +0200172 /* next is a node that will actually be printed */
Michal Vasko568b1952018-01-30 15:53:30 +0100173 has_next = tree_sibling_is_valid_child(node, 0, opts->module, aug_parent, node->nodetype);
Michal Vasko14410462015-06-05 15:08:54 +0200174
Michal Vasko568b1952018-01-30 15:53:30 +0100175 /* set level indent */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200176 if (has_next && !next_is_case) {
Michal Vasko03d8cd22018-02-08 14:48:28 +0100177 opts->indent |= (uint64_t)1ULL << (level - 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200178 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200179}
180
Michal Vasko568b1952018-01-30 15:53:30 +0100181static uint16_t
182tree_get_max_name_len(const struct lys_node *sibling, const struct lys_node *aug_parent, int type_mask,
183 tp_opts *opts)
Michal Vasko8d479bd2015-06-05 10:50:03 +0200184{
Michal Vasko1e62a092015-12-01 12:27:20 +0100185 const struct lys_node *sub;
Michal Vasko568b1952018-01-30 15:53:30 +0100186 struct lys_module *nodemod;
187 unsigned int max_name_len = 0, name_len;
Michal Vasko8d479bd2015-06-05 10:50:03 +0200188
Michal Vasko568b1952018-01-30 15:53:30 +0100189 LY_TREE_FOR(sibling, sub) {
190 if (opts->module->type && (sub->module != opts->module)) {
Michal Vaskobda37192016-02-15 11:09:13 +0100191 /* when printing submodule, we are only concerned with its own data (they are in the module data) */
192 continue;
193 }
Michal Vasko568b1952018-01-30 15:53:30 +0100194 if (aug_parent && (sub->parent != aug_parent)) {
195 /* when printing augment children, skip other target children */
196 continue;
197 }
198 if (!(sub->nodetype & type_mask)) {
199 /* this sibling will not be printed */
200 continue;
201 }
Michal Vaskobda37192016-02-15 11:09:13 +0100202
Michal Vasko568b1952018-01-30 15:53:30 +0100203 if ((sub->nodetype == LYS_USES) && !(opts->options & LYS_OUTOPT_TREE_USES)) {
204 name_len = tree_get_max_name_len(sub->child, NULL, type_mask, opts);
205 } else {
206 nodemod = lys_node_module(sub);
207 name_len = strlen(sub->name);
208 if (lys_main_module(opts->module) != nodemod) {
209 /* ":" */
210 ++name_len;
211 if (opts->options & LYS_OUTOPT_TREE_RFC) {
212 name_len += strlen(nodemod->prefix);
213 } else {
214 name_len += strlen(nodemod->name);
215 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200216 }
Michal Vasko568b1952018-01-30 15:53:30 +0100217
218 /* add characters for optional opts */
219 switch (sub->nodetype) {
220 case LYS_LEAF:
221 case LYS_LEAFLIST:
222 case LYS_LIST:
223 case LYS_ANYDATA:
224 case LYS_ANYXML:
225 case LYS_CONTAINER:
226 case LYS_CASE:
227 ++name_len;
228 break;
229 case LYS_CHOICE:
230 /* choice is longer :-/ */
231 name_len += 2;
232 if (!(sub->flags & LYS_MAND_TRUE)) {
233 ++name_len;
234 }
235 break;
236 default:
237 break;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200238 }
239 }
Michal Vasko568b1952018-01-30 15:53:30 +0100240
241 if (name_len > max_name_len) {
242 max_name_len = name_len;
243 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200244 }
Michal Vasko8d479bd2015-06-05 10:50:03 +0200245
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200246 return max_name_len;
Michal Vasko8d479bd2015-06-05 10:50:03 +0200247}
248
Michal Vasko568b1952018-01-30 15:53:30 +0100249static int
250tree_leaf_is_mandatory(const struct lys_node *node)
Michal Vasko5ed10f12015-06-04 10:04:57 +0200251{
Michal Vasko568b1952018-01-30 15:53:30 +0100252 const struct lys_node *parent;
Radek Krejcib8048692015-08-05 13:36:34 +0200253 struct lys_node_list *list;
Michal Vasko568b1952018-01-30 15:53:30 +0100254 uint16_t i;
Michal Vasko449afde2015-06-04 16:06:49 +0200255
Michal Vaskodcf98e62016-05-05 17:53:53 +0200256 for (parent = lys_parent(node); parent && parent->nodetype == LYS_USES; parent = lys_parent(parent));
Radek Krejci69372e22015-08-13 09:55:27 +0200257 if (parent && parent->nodetype == LYS_LIST) {
Radek Krejcib8048692015-08-05 13:36:34 +0200258 list = (struct lys_node_list *)parent;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200259 for (i = 0; i < list->keys_size; i++) {
Michal Vasko568b1952018-01-30 15:53:30 +0100260 if (list->keys[i] == (struct lys_node_leaf *)node) {
261 return 1;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200262 }
263 }
264 }
Michal Vasko449afde2015-06-04 16:06:49 +0200265
Michal Vasko568b1952018-01-30 15:53:30 +0100266 return 0;
Michal Vasko5ed10f12015-06-04 10:04:57 +0200267}
268
Michal Vasko568b1952018-01-30 15:53:30 +0100269static int
270tree_print_wrap(struct lyout *out, int level, int line_printed, uint8_t indent, uint16_t len, tp_opts *opts)
Michal Vasko5ed10f12015-06-04 10:04:57 +0200271{
Michal Vasko568b1952018-01-30 15:53:30 +0100272 if (opts->line_length && (line_printed + indent + len > opts->line_length)) {
273 ly_print(out, "\n");
274 line_printed = tree_print_indent(out, level, opts);
275 /* 3 for config + space */
276 line_printed += ly_print(out, "%*s", 3 + LY_TREE_WRAP_INDENT, "");
277 } else {
278 line_printed += ly_print(out, "%*s", indent, "");
279 }
Michal Vasko449afde2015-06-04 16:06:49 +0200280
Michal Vasko568b1952018-01-30 15:53:30 +0100281 return line_printed;
282}
Michal Vaskob5c75d72015-06-15 12:16:52 +0200283
Michal Vasko568b1952018-01-30 15:53:30 +0100284static int
285tree_print_prefix(struct lyout *out, const struct lys_node *node, tp_opts *opts)
286{
287 uint16_t ret = 0;
288 const struct lys_module *nodemod;
Michal Vaskob5c75d72015-06-15 12:16:52 +0200289
Michal Vaskobda37192016-02-15 11:09:13 +0100290 nodemod = lys_node_module(node);
Michal Vasko568b1952018-01-30 15:53:30 +0100291 if (lys_main_module(opts->module) != nodemod) {
292 if (opts->options & LYS_OUTOPT_TREE_RFC) {
293 ret = ly_print(out, "%s:", nodemod->prefix);
294 } else {
295 ret = ly_print(out, "%s:", nodemod->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200296 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200297 }
Michal Vaskob5c75d72015-06-15 12:16:52 +0200298
Michal Vasko568b1952018-01-30 15:53:30 +0100299 return ret;
300}
Michal Vasko28c6b572015-06-18 12:43:31 +0200301
Michal Vasko568b1952018-01-30 15:53:30 +0100302static int
303tree_print_type(struct lyout *out, const struct lys_type *type, int options, const char **out_str)
304{
305 struct lys_module *type_mod = ((struct lys_tpdf *)type->parent)->module;
306 const char *str;
307 char *tmp;
308 int printed;
Michal Vasko5ed10f12015-06-04 10:04:57 +0200309
Michal Vasko568b1952018-01-30 15:53:30 +0100310 if ((type->base == LY_TYPE_LEAFREF) && !type->der->module) {
311 if (options & LYS_OUTOPT_TREE_NO_LEAFREF) {
312 if (out_str) {
313 printed = 7;
314 *out_str = lydict_insert(type_mod->ctx, "leafref", printed);
315 } else {
316 printed = ly_print(out, "leafref");
317 }
318 } else {
319 if (options & LYS_OUTOPT_TREE_RFC) {
320 str = transform_json2schema(type_mod, type->info.lref.path);
321 if (out_str) {
322 printed = 3 + strlen(str);
323 tmp = malloc(printed + 1);
Michal Vasko53b7da02018-02-13 15:28:42 +0100324 LY_CHECK_ERR_RETURN(!tmp, LOGMEM(type_mod->ctx), 0);
Michal Vasko568b1952018-01-30 15:53:30 +0100325 sprintf(tmp, "-> %s", str);
326 *out_str = lydict_insert_zc(type_mod->ctx, tmp);
327 } else {
328 printed = ly_print(out, "-> %s", str);
329 }
330 lydict_remove(type_mod->ctx, str);
331 } else {
332 if (out_str) {
333 printed = 3 + strlen(type->info.lref.path);
334 tmp = malloc(printed + 1);
Michal Vasko53b7da02018-02-13 15:28:42 +0100335 LY_CHECK_ERR_RETURN(!tmp, LOGMEM(type_mod->ctx), 0);
Michal Vasko568b1952018-01-30 15:53:30 +0100336 sprintf(tmp, "-> %s", type->info.lref.path);
337 *out_str = lydict_insert_zc(type_mod->ctx, tmp);
338 } else {
339 printed = ly_print(out, "-> %s", type->info.lref.path);
340 }
341 }
Michal Vasko3510f892016-02-15 14:33:52 +0100342 }
Michal Vasko568b1952018-01-30 15:53:30 +0100343 } else if (!lys_type_is_local(type)) {
344 if (options & LYS_OUTOPT_TREE_RFC) {
345 str = transform_module_name2import_prefix(type_mod, type->der->module->name);
346 if (out_str) {
347 printed = strlen(str) + 1 + strlen(type->der->name);
348 tmp = malloc(printed + 1);
Michal Vasko53b7da02018-02-13 15:28:42 +0100349 LY_CHECK_ERR_RETURN(!tmp, LOGMEM(type_mod->ctx), 0);
Michal Vasko568b1952018-01-30 15:53:30 +0100350 sprintf(tmp, "%s:%s", str, type->der->name);
351 *out_str = lydict_insert_zc(type_mod->ctx, tmp);
352 } else {
353 printed = ly_print(out, "%s:%s", str, type->der->name);
354 }
355 } else {
356 if (out_str) {
357 printed = strlen(type->der->module->name) + 1 + strlen(type->der->name);
358 tmp = malloc(printed + 1);
Michal Vasko53b7da02018-02-13 15:28:42 +0100359 LY_CHECK_ERR_RETURN(!tmp, LOGMEM(type_mod->ctx), 0);
Michal Vasko568b1952018-01-30 15:53:30 +0100360 sprintf(tmp, "%s:%s", type->der->module->name, type->der->name);
361 *out_str = lydict_insert_zc(type_mod->ctx, tmp);
362 } else {
363 printed = ly_print(out, "%s:%s", type->der->module->name, type->der->name);
364 }
365 }
366 } else {
367 if (out_str) {
368 printed = strlen(type->der->name);
369 *out_str = lydict_insert(type_mod->ctx, type->der->name, printed);
370 } else {
371 printed = ly_print(out, "%s", type->der->name);
372 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200373 }
Michal Vasko568b1952018-01-30 15:53:30 +0100374
375 return printed;
Michal Vasko5ed10f12015-06-04 10:04:57 +0200376}
377
Michal Vasko568b1952018-01-30 15:53:30 +0100378static int
379tree_print_config(struct lyout *out, const struct lys_node *node, int spec_config)
Michal Vasko5ed10f12015-06-04 10:04:57 +0200380{
Michal Vasko568b1952018-01-30 15:53:30 +0100381 int ret;
Radek Krejci7e97c352015-06-19 16:26:34 +0200382
Michal Vasko568b1952018-01-30 15:53:30 +0100383 switch (node->nodetype) {
384 case LYS_RPC:
385 case LYS_ACTION:
386 return ly_print(out, "-x ");
387 case LYS_NOTIF:
388 return ly_print(out, "-n ");
389 case LYS_USES:
390 return ly_print(out, "-u ");
391 case LYS_CASE:
392 return ly_print(out, ":(");
393 default:
394 break;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200395 }
Michal Vasko568b1952018-01-30 15:53:30 +0100396
Michal Vasko0fc09972018-01-30 16:24:58 +0100397 if (spec_config == 1) {
Michal Vasko568b1952018-01-30 15:53:30 +0100398 ret = ly_print(out, "-w ");
399 } else if (spec_config == 2) {
400 ret = ly_print(out, "ro ");
Michal Vasko0fc09972018-01-30 16:24:58 +0100401 } else {
402 ret = ly_print(out, "%s ", (node->flags & LYS_CONFIG_W) ? "rw" : (node->flags & LYS_CONFIG_R) ? "ro" : "--");
Michal Vasko568b1952018-01-30 15:53:30 +0100403 }
404
405 if (node->nodetype == LYS_CHOICE) {
406 ret += ly_print(out, "(");
407 }
408 return ret;
Michal Vasko5ed10f12015-06-04 10:04:57 +0200409}
410
Michal Vasko568b1952018-01-30 15:53:30 +0100411static int
412tree_print_features(struct lyout *out, struct lys_iffeature *iff1, uint8_t iff1_size, struct lys_iffeature *iff2,
413 uint8_t iff2_size, tp_opts *opts, const char **out_str)
Michal Vaskob5c75d72015-06-15 12:16:52 +0200414{
Michal Vasko568b1952018-01-30 15:53:30 +0100415 int i, printed;
416 struct lyout *o;
Radek Krejci7e97c352015-06-19 16:26:34 +0200417
Michal Vasko568b1952018-01-30 15:53:30 +0100418 if (!iff1_size && !iff2_size) {
419 return 0;
420 }
421
422 if (out_str) {
423 o = malloc(sizeof *o);
Michal Vasko53b7da02018-02-13 15:28:42 +0100424 LY_CHECK_ERR_RETURN(!o, LOGMEM(NULL), 0);
Michal Vasko568b1952018-01-30 15:53:30 +0100425 o->type = LYOUT_MEMORY;
426 o->method.mem.buf = NULL;
427 o->method.mem.len = 0;
428 o->method.mem.size = 0;
429 } else {
430 o = out;
431 }
432
433 printed = ly_print(o, "{");
434 for (i = 0; i < iff1_size; i++) {
435 if (i > 0) {
436 printed += ly_print(o, ",");
437 }
438 printed += ly_print_iffeature(o, opts->module, &iff1[i], opts->options & LYS_OUTOPT_TREE_RFC ? 2 : 1);
439 }
440 for (i = 0; i < iff2_size; i++) {
441 if (i > 0) {
442 printed += ly_print(o, ",");
443 }
444 printed += ly_print_iffeature(o, opts->module, &iff2[i], opts->options & LYS_OUTOPT_TREE_RFC ? 2 : 1);
445 }
446 printed += ly_print(o, "}?");
447
448 if (out_str) {
449 *out_str = lydict_insert_zc(opts->module->ctx, o->method.mem.buf);
450 free(o);
451 }
452
453 return printed;
454}
455
456static int
457tree_print_keys(struct lyout *out, struct lys_node_leaf **keys, uint8_t keys_size, tp_opts *opts, const char **out_str)
458{
459 int i, printed;
460 struct lyout *o;
461
462 if (!keys_size) {
463 return 0;
464 }
465
466 if (out_str) {
467 o = malloc(sizeof *o);
Michal Vasko53b7da02018-02-13 15:28:42 +0100468 LY_CHECK_ERR_RETURN(!o, LOGMEM(NULL), 0);
Michal Vasko568b1952018-01-30 15:53:30 +0100469 o->type = LYOUT_MEMORY;
470 o->method.mem.buf = NULL;
471 o->method.mem.len = 0;
472 o->method.mem.size = 0;
473 } else {
474 o = out;
475 }
476
477 printed = ly_print(o, "[");
478 for (i = 0; i < keys_size; i++) {
479 printed += ly_print(o, "%s%s", keys[i]->name, i + 1 < keys_size ? " " : "]");
480 }
481
482 if (out_str) {
483 *out_str = lydict_insert_zc(opts->module->ctx, o->method.mem.buf);
484 free(o);
485 }
486
487 return printed;
488}
489
490/**
491 * @brief Print schema node in YANG tree diagram formatting.
492 *
493 * @param[in] out libyang output.
494 * @param[in] level Current level of depth.
495 * @param[in] max_name_len Maximal name length of all the siblings (relevant only for nodes with type).
496 * @param[in] node Schema node to print.
497 * @param[in] mask Type mask of children nodes to be printed.
498 * @param[in] aug_parent Augment node parent in case we are printing its direct children.
499 * @param[in] opts Tree printer options structure.
500 */
501static void
502tree_print_snode(struct lyout *out, int level, uint16_t max_name_len, const struct lys_node *node, int mask,
503 const struct lys_node *aug_parent, int subtree, tp_opts *opts)
504{
505 struct lys_node *sub;
506 int line_len, node_len, child_mask;
507 uint8_t text_len, text_indent;
508 uint16_t max_child_len;
509 const char *text_str;
510
511 /* disabled/not printed node */
512 if (lys_is_disabled(node, (node->parent && node->parent->nodetype == LYS_AUGMENT) ? 1 : 0) || !(node->nodetype & mask)) {
Michal Vaskoefbb3192015-07-08 10:35:00 +0200513 return;
Radek Krejci7e97c352015-06-19 16:26:34 +0200514 }
Michal Vaskob5c75d72015-06-15 12:16:52 +0200515
Michal Vaskoe0f11be2018-03-02 14:08:22 +0100516 /* implicit input/output/case */
517 if (((node->nodetype & mask) & (LYS_INPUT | LYS_OUTPUT | LYS_CASE)) && (node->flags & LYS_IMPLICIT)) {
518 if ((node->nodetype != LYS_CASE) || lys_is_disabled(node->child, 0)) {
519 return;
520 }
Radek Krejci7e97c352015-06-19 16:26:34 +0200521 }
Michal Vaskoe03bfbb2015-06-16 09:07:49 +0200522
Michal Vasko568b1952018-01-30 15:53:30 +0100523 /* special uses and grouping handling */
Radek Krejci1d82ef62015-08-07 14:44:40 +0200524 switch (node->nodetype & mask) {
Michal Vasko568b1952018-01-30 15:53:30 +0100525 case LYS_USES:
526 if (opts->options & LYS_OUTOPT_TREE_USES) {
527 break;
528 }
529 /* fallthrough */
530 case LYS_GROUPING:
531 goto print_children;
Michal Vaskob3d6d192018-11-05 10:02:10 +0100532 case LYS_ANYXML:
533 if (!lys_parent(node) && !strcmp(node->name, "config") && !strcmp(node->module->name, "ietf-netconf")) {
534 /* node added by libyang, not actually in the model */
535 return;
536 }
537 break;
Michal Vasko568b1952018-01-30 15:53:30 +0100538 default:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200539 break;
Michal Vasko568b1952018-01-30 15:53:30 +0100540 }
541
542 /* print indent */
543 line_len = tree_print_indent(out, level, opts);
544 /* print status */
545 line_len += ly_print(out, "%s--", (node->flags & LYS_STATUS_DEPRC ? "x" : (node->flags & LYS_STATUS_OBSLT ? "o" : "+")));
546 /* print config flags (or special opening for case, choice) */
547 line_len += tree_print_config(out, node, opts->spec_config);
548 /* print optionally prefix */
549 node_len = tree_print_prefix(out, node, opts);
550 /* print name */
551 node_len += ly_print(out, node->name);
552
553 /* print one-character opts */
554 switch (node->nodetype & mask) {
Radek Krejci76512572015-08-04 09:47:08 +0200555 case LYS_LEAF:
Michal Vasko568b1952018-01-30 15:53:30 +0100556 if (!(node->flags & LYS_MAND_TRUE) && !tree_leaf_is_mandatory(node)) {
557 node_len += ly_print(out, "?");
558 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200559 break;
Michal Vasko568b1952018-01-30 15:53:30 +0100560 case LYS_ANYDATA:
561 case LYS_ANYXML:
562 if (!(node->flags & LYS_MAND_TRUE)) {
563 node_len += ly_print(out, "?");
564 }
565 break;
566 case LYS_CONTAINER:
567 if (((struct lys_node_container *)node)->presence) {
568 node_len += ly_print(out, "!");
569 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200570 break;
Radek Krejci76512572015-08-04 09:47:08 +0200571 case LYS_LIST:
Michal Vasko568b1952018-01-30 15:53:30 +0100572 case LYS_LEAFLIST:
573 node_len += ly_print(out, "*");
Michal Vaskoca7cbc42016-07-01 11:36:53 +0200574 break;
Michal Vaskod4742e62016-02-15 15:11:55 +0100575 case LYS_CASE:
Michal Vasko568b1952018-01-30 15:53:30 +0100576 /* kinda shady, but consistent in a way */
577 node_len += ly_print(out, ")");
578 break;
579 case LYS_CHOICE:
580 node_len += ly_print(out, ")");
581 if (!(node->flags & LYS_MAND_TRUE)) {
582 node_len += ly_print(out, "?");
583 }
584 break;
585 default:
586 break;
587 }
588 line_len += node_len;
589
590 /**
591 * wrapped print
592 */
593
594 /* learn next level indent (there is never a sibling for subtree) */
595 ++level;
596 if (!subtree) {
597 tree_next_indent(level, node, aug_parent, opts);
598 }
599
600 /* print type/keys */
601 switch (node->nodetype & mask) {
602 case LYS_LEAF:
603 case LYS_LEAFLIST:
604 assert(max_name_len);
605 text_indent = LY_TREE_TYPE_INDENT + (uint8_t)(max_name_len - node_len);
606 text_len = tree_print_type(out, &((struct lys_node_leaf *)node)->type, opts->options, &text_str);
607 line_len = tree_print_wrap(out, level, line_len, text_indent, text_len, opts);
608 line_len += ly_print(out, text_str);
609 lydict_remove(opts->module->ctx, text_str);
610 break;
611 case LYS_ANYDATA:
612 assert(max_name_len);
613 text_indent = LY_TREE_TYPE_INDENT + (uint8_t)(max_name_len - node_len);
614 line_len = tree_print_wrap(out, level, line_len, text_indent, 7, opts);
615 line_len += ly_print(out, "anydata");
616 break;
617 case LYS_ANYXML:
618 assert(max_name_len);
619 text_indent = LY_TREE_TYPE_INDENT + (uint8_t)(max_name_len - node_len);
620 line_len = tree_print_wrap(out, level, line_len, text_indent, 6, opts);
621 line_len += ly_print(out, "anyxml");
622 break;
623 case LYS_LIST:
624 text_len = tree_print_keys(out, ((struct lys_node_list *)node)->keys, ((struct lys_node_list *)node)->keys_size,
625 opts, &text_str);
626 if (text_len) {
627 line_len = tree_print_wrap(out, level, line_len, 1, text_len, opts);
628 line_len += ly_print(out, text_str);
629 lydict_remove(opts->module->ctx, text_str);
630 }
631 break;
632 default:
633 break;
634 }
635
636 /* print default */
637 if (!(opts->options & LYS_OUTOPT_TREE_RFC)) {
638 switch (node->nodetype & mask) {
639 case LYS_LEAF:
640 text_str = ((struct lys_node_leaf *)node)->dflt;
641 if (text_str) {
642 line_len = tree_print_wrap(out, level, line_len, 1, 2 + strlen(text_str), opts);
643 line_len += ly_print(out, "<%s>", text_str);
644 }
645 break;
646 case LYS_CHOICE:
647 sub = ((struct lys_node_choice *)node)->dflt;
648 if (sub) {
649 line_len = tree_print_wrap(out, level, line_len, 1, 2 + strlen(sub->name), opts);
650 line_len += ly_print(out, "<%s>", sub->name);
651 }
652 break;
653 default:
654 break;
655 }
656 }
657
658 /* print if-features */
659 switch (node->nodetype & mask) {
660 case LYS_CONTAINER:
661 case LYS_LIST:
662 case LYS_CHOICE:
663 case LYS_CASE:
664 case LYS_ANYDATA:
665 case LYS_ANYXML:
666 case LYS_LEAF:
667 case LYS_LEAFLIST:
668 case LYS_RPC:
669 case LYS_ACTION:
670 case LYS_NOTIF:
671 case LYS_USES:
672 if (node->parent && (node->parent->nodetype == LYS_AUGMENT)) {
673 /* if-features from an augment are de facto inherited */
674 text_len = tree_print_features(out, node->iffeature, node->iffeature_size,
675 node->parent->iffeature, node->parent->iffeature_size, opts, &text_str);
676 } else {
677 text_len = tree_print_features(out, node->iffeature, node->iffeature_size, NULL, 0, opts, &text_str);
678 }
679 if (text_len) {
680 line_len = tree_print_wrap(out, level, line_len, 1, text_len, opts);
681 line_len += ly_print(out, text_str);
682 lydict_remove(opts->module->ctx, text_str);
683 }
684 break;
685 default:
686 /* only grouping */
687 break;
688 }
689
690 /* this node is finished printing */
691 ly_print(out, "\n");
692
693 if ((subtree == 1) || ((node->nodetype & mask) == LYS_USES)) {
694 /* we are printing subtree parents, finish here (or uses option) */
695 return;
696 }
697
698 /* set special config flag */
699 switch (node->nodetype & mask) {
700 case LYS_INPUT:
701 opts->spec_config = 1;
702 break;
703 case LYS_OUTPUT:
704 case LYS_NOTIF:
705 opts->spec_config = 2;
706 break;
707 default:
708 break;
709 }
710
711print_children:
712 /* set child mask and learn the longest child name (needed only if a child can have type) */
713 switch (node->nodetype & mask) {
714 case LYS_LEAF:
715 case LYS_LEAFLIST:
716 case LYS_ANYDATA:
717 case LYS_ANYXML:
718 child_mask = 0;
719 max_child_len = 0;
720 break;
721 case LYS_RPC:
722 case LYS_ACTION:
723 child_mask = LYS_INPUT | LYS_OUTPUT;
724 max_child_len = 0;
725 break;
726 case LYS_CHOICE:
727 child_mask = LYS_CASE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA;
728 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
729 break;
730 case LYS_CASE:
731 case LYS_NOTIF:
732 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES;
733 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
734 break;
735 case LYS_INPUT:
736 case LYS_OUTPUT:
737 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES;
738 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
739 break;
740 case LYS_USES:
741 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES | LYS_ACTION | LYS_NOTIF;
742 /* inherit the name length from the parent, it does not change */
743 max_child_len = max_name_len;
744 break;
745 case LYS_CONTAINER:
746 case LYS_LIST:
747 case LYS_GROUPING:
748 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES | LYS_ACTION | LYS_NOTIF;
749 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
750 break;
Michal Vaskod1bf7c42018-02-15 08:38:49 +0100751 default:
752 child_mask = 0;
753 max_child_len = 0;
754 LOGINT(node->module->ctx);
755 break;
Michal Vasko568b1952018-01-30 15:53:30 +0100756 }
757
758 /* print descendants (children) */
759 if (child_mask) {
760 LY_TREE_FOR(node->child, sub) {
761 /* submodule, foreign augments */
762 if (opts->module->type && (sub->parent != node) && (sub->module != opts->module)) {
763 continue;
764 }
765 tree_print_snode(out, level, max_child_len, sub, child_mask, NULL, 0, opts);
766 }
767 }
768
769 /* reset special config flag */
770 switch (node->nodetype & mask) {
771 case LYS_INPUT:
772 case LYS_OUTPUT:
773 case LYS_NOTIF:
774 opts->spec_config = 0;
Michal Vaskod4742e62016-02-15 15:11:55 +0100775 break;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200776 default:
777 break;
778 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200779}
780
Michal Vasko568b1952018-01-30 15:53:30 +0100781static void
782tree_print_subtree(struct lyout *out, const struct lys_node *node, tp_opts *opts)
783{
784 unsigned int depth, i, j;
785 int level = 0;
fredgan7622caf2019-12-10 16:54:37 +0800786 uint16_t max_child_len;
Michal Vasko568b1952018-01-30 15:53:30 +0100787 const struct lys_node *parent;
788
789 /* learn the depth of the node */
790 depth = 0;
791 parent = node;
792 while (lys_parent(parent)) {
793 if (lys_parent(parent)->nodetype != LYS_USES) {
794 ++depth;
795 }
796 parent = lys_parent(parent);
797 }
798
799 if (parent->nodetype == LYS_RPC) {
800 ly_print(out, "\n%*srpcs:\n", LY_TREE_MOD_DATA_INDENT, "");
801 opts->base_indent = LY_TREE_OP_DATA_INDENT;
802 } else if (parent->nodetype == LYS_NOTIF) {
803 ly_print(out, "\n%*snotifications:\n", LY_TREE_MOD_DATA_INDENT, "");
804 opts->base_indent = LY_TREE_OP_DATA_INDENT;
805 }
806
807 /* print all the parents */
808 if (depth) {
809 i = depth;
810 do {
811 parent = node;
812 for (j = 0; j < i; ++j) {
813 do {
814 parent = lys_parent(parent);
815 } while (parent->nodetype == LYS_USES);
816 }
817
818 tree_print_snode(out, level, 0, parent, LYS_CONTAINER | LYS_LIST | LYS_NOTIF | LYS_RPC | LYS_ACTION
819 | LYS_INPUT | LYS_OUTPUT, NULL, 1, opts);
820
821 ++level;
822 --i;
823 } while (i);
824 }
825
826 /* print the node and its descendants */
fredgan7622caf2019-12-10 16:54:37 +0800827 max_child_len = tree_get_max_name_len(node, NULL, LYS_LEAF|LYS_LEAFLIST|LYS_ANYDATA, opts);
828 tree_print_snode(out, level, max_child_len, node, LYS_ANY, NULL, 2, opts);
Michal Vasko568b1952018-01-30 15:53:30 +0100829}
830
831static int
832tree_print_aug_target(struct lyout *out, int line_printed, uint8_t indent, const char *path, tp_opts *opts)
833{
834 int printed, is_last, len;
835 const char *cur, *next;
836
837 printed = line_printed;
838 cur = path;
839 do {
840 next = strchr(cur + 1, '/');
841 if (!next) {
842 len = strlen(cur) + 1;
843 is_last = 1;
844 } else {
845 len = next - cur;
846 is_last = 0;
847 }
848
849 if (opts->line_length && cur != path && (printed + len > opts->line_length)) {
850 /* line_printed is treated as the base indent */
851 printed = ly_print(out, "\n%*s", line_printed + indent, "");
852 /* minus the newline */
853 --printed;
854 }
855 printed += ly_print(out, "%.*s%s", len, cur, is_last ? ":" : "");
856
857 cur = next;
858 } while (!is_last);
859
860 return printed;
861}
862
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200863int
Michal Vasko568b1952018-01-30 15:53:30 +0100864tree_print_model(struct lyout *out, const struct lys_module *module, const char *target_schema_path,
865 int ll, int options)
Michal Vasko5ed10f12015-06-04 10:04:57 +0200866{
Michal Vaskofb446d52018-08-01 11:42:42 +0200867 struct lys_node *node = NULL, *data, *aug;
Michal Vasko568b1952018-01-30 15:53:30 +0100868 struct ly_set *set;
869 uint16_t max_child_len;
870 int have_rpcs = 0, have_notifs = 0, have_grps = 0, have_augs = 0, printed;
871 const char *str;
872 int i, mask;
873 tp_opts opts;
874
875 memset(&opts, 0, sizeof opts);
876 opts.module = module;
877 opts.line_length = ll;
878 opts.options = options;
879
880 /* we are printing only a subtree */
881 if (target_schema_path) {
882 set = lys_find_path(module, NULL, target_schema_path);
883 if (!set) {
884 return EXIT_FAILURE;
885 } else if (set->number != 1) {
Michal Vasko53b7da02018-02-13 15:28:42 +0100886 LOGVAL(module->ctx, LYE_PATH_INNODE, LY_VLOG_NONE, NULL);
Michal Vasko568b1952018-01-30 15:53:30 +0100887 if (set->number == 0) {
Michal Vasko53b7da02018-02-13 15:28:42 +0100888 LOGVAL(module->ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Schema path \"%s\" did not match any nodes.", target_schema_path);
Michal Vasko568b1952018-01-30 15:53:30 +0100889 } else {
Michal Vasko53b7da02018-02-13 15:28:42 +0100890 LOGVAL(module->ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Schema path \"%s\" matched more nodes.", target_schema_path);
Michal Vasko568b1952018-01-30 15:53:30 +0100891 }
892 ly_set_free(set);
893 return EXIT_FAILURE;
894 }
895
896 node = set->set.s[0];
897 ly_set_free(set);
898 }
Michal Vasko253035f2015-12-17 16:58:13 +0100899
Michal Vaskofc6ac172015-07-07 09:46:46 +0200900 if (module->type) {
Michal Vasko568b1952018-01-30 15:53:30 +0100901 ly_print(out, "submodule: %s", module->name);
Michal Vaskobda37192016-02-15 11:09:13 +0100902 data = ((struct lys_submodule *)module)->belongsto->data;
Michal Vasko568b1952018-01-30 15:53:30 +0100903 if (options & LYS_OUTOPT_TREE_RFC) {
904 ly_print(out, "\n");
905 } else {
906 ly_print(out, " (belongs-to %s)\n", ((struct lys_submodule *)module)->belongsto->name);
907 }
Michal Vaskofc6ac172015-07-07 09:46:46 +0200908 } else {
Radek Krejci76b07902015-10-09 09:11:25 +0200909 ly_print(out, "module: %s\n", module->name);
Michal Vaskobda37192016-02-15 11:09:13 +0100910 data = module->data;
Michal Vaskofc6ac172015-07-07 09:46:46 +0200911 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200912
Michal Vasko568b1952018-01-30 15:53:30 +0100913 /* only subtree */
914 if (target_schema_path) {
915 opts.base_indent = LY_TREE_MOD_DATA_INDENT;
916 tree_print_subtree(out, node, &opts);
917 return EXIT_SUCCESS;
918 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200919
Michal Vasko568b1952018-01-30 15:53:30 +0100920 /* module */
921 opts.base_indent = LY_TREE_MOD_DATA_INDENT;
922 mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES;
923 max_child_len = tree_get_max_name_len(data, NULL, mask, &opts);
Michal Vaskobda37192016-02-15 11:09:13 +0100924 LY_TREE_FOR(data, node) {
Michal Vasko568b1952018-01-30 15:53:30 +0100925 if (opts.module->type && (node->module != opts.module)) {
Michal Vaskobda37192016-02-15 11:09:13 +0100926 /* we're printing the submodule only */
927 continue;
928 }
929
Michal Vasko568b1952018-01-30 15:53:30 +0100930 switch (node->nodetype) {
Radek Krejci92720552015-10-05 15:28:27 +0200931 case LYS_RPC:
Radek Krejciadf7a8e2015-12-10 13:11:17 +0100932 if (!lys_is_disabled(node, 0)) {
933 have_rpcs++;
934 }
Radek Krejci92720552015-10-05 15:28:27 +0200935 break;
936 case LYS_NOTIF:
Radek Krejciadf7a8e2015-12-10 13:11:17 +0100937 if (!lys_is_disabled(node, 0)) {
938 have_notifs++;
939 }
Radek Krejci92720552015-10-05 15:28:27 +0200940 break;
Radek Krejcid5bf08e2017-01-25 11:35:04 +0100941 case LYS_GROUPING:
Michal Vasko568b1952018-01-30 15:53:30 +0100942 if ((options & LYS_OUTOPT_TREE_GROUPING) && !lys_is_disabled(node, 0)) {
Radek Krejcid5bf08e2017-01-25 11:35:04 +0100943 have_grps++;
944 }
945 break;
Radek Krejci92720552015-10-05 15:28:27 +0200946 default:
Michal Vasko568b1952018-01-30 15:53:30 +0100947 tree_print_snode(out, 0, max_child_len, node, mask, NULL, 0, &opts);
Radek Krejci92720552015-10-05 15:28:27 +0200948 break;
949 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200950 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200951
Michal Vasko568b1952018-01-30 15:53:30 +0100952 /* all remaining nodes printed with operation indent */
953 opts.base_indent = LY_TREE_OP_DATA_INDENT;
954
955 /* augments */
Radek Krejcid49202f2016-02-11 12:53:39 +0100956 for (i = 0; i < module->augment_size; i++) {
Michal Vasko861ee6d2016-02-15 15:11:31 +0100957 if ((module->type && (module->augment[i].target->module == module))
Michal Vasko9089c8d2016-10-25 09:23:53 +0200958 || (!module->type && (lys_node_module(module->augment[i].target) == module))
959 || lys_is_disabled((struct lys_node *)&module->augment[i], 0)) {
Michal Vaskoe79abe82016-02-15 12:33:23 +0100960 /* submodule, target is our submodule or module, target is in our module or any submodules */
961 continue;
962 }
963
Michal Vasko568b1952018-01-30 15:53:30 +0100964 if (!have_augs) {
965 ly_print(out, "\n");
966 have_augs = 1;
967 }
968
969 printed = ly_print(out, "%*saugment ", LY_TREE_MOD_DATA_INDENT, "");
970 if (options & LYS_OUTOPT_TREE_RFC) {
971 str = transform_json2schema(module, module->augment[i].target_name);
972 tree_print_aug_target(out, printed, LY_TREE_WRAP_INDENT, str, &opts);
973 lydict_remove(module->ctx, str);
974 } else {
975 tree_print_aug_target(out, printed, LY_TREE_WRAP_INDENT, module->augment[i].target_name, &opts);
976 }
977 ly_print(out, "\n");
978
Michal Vaskofb446d52018-08-01 11:42:42 +0200979 aug = (struct lys_node *)&module->augment[i];
Michal Vasko568b1952018-01-30 15:53:30 +0100980 mask = LYS_CHOICE | LYS_CASE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES
981 | LYS_ACTION | LYS_NOTIF;
Michal Vaskofb446d52018-08-01 11:42:42 +0200982 max_child_len = tree_get_max_name_len(aug->child, aug, mask, &opts);
983 LY_TREE_FOR(aug->child, node) {
Michal Vaskod4742e62016-02-15 15:11:55 +0100984 /* submodule, foreign augments */
Michal Vaskofb446d52018-08-01 11:42:42 +0200985 if (node->parent != aug) {
Michal Vaskod4742e62016-02-15 15:11:55 +0100986 continue;
987 }
Michal Vaskofb446d52018-08-01 11:42:42 +0200988 tree_print_snode(out, 0, max_child_len, node, mask, aug, 0, &opts);
Radek Krejcid49202f2016-02-11 12:53:39 +0100989 }
990 }
991
Michal Vasko568b1952018-01-30 15:53:30 +0100992 /* rpcs */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200993 if (have_rpcs) {
Michal Vasko568b1952018-01-30 15:53:30 +0100994 ly_print(out, "\n%*srpcs:\n", LY_TREE_MOD_DATA_INDENT, "");
995
Michal Vaskobda37192016-02-15 11:09:13 +0100996 LY_TREE_FOR(data, node) {
Michal Vasko568b1952018-01-30 15:53:30 +0100997 tree_print_snode(out, 0, 0, node, LYS_RPC, NULL, 0, &opts);
Michal Vaskob5c75d72015-06-15 12:16:52 +0200998 }
999 }
Michal Vasko449afde2015-06-04 16:06:49 +02001000
Michal Vasko568b1952018-01-30 15:53:30 +01001001 /* notifications */
Michal Vaskoe03bfbb2015-06-16 09:07:49 +02001002 if (have_notifs) {
Michal Vasko568b1952018-01-30 15:53:30 +01001003 ly_print(out, "\n%*snotifications:\n", LY_TREE_MOD_DATA_INDENT, "");
1004
Michal Vaskobda37192016-02-15 11:09:13 +01001005 LY_TREE_FOR(data, node) {
Michal Vasko568b1952018-01-30 15:53:30 +01001006 tree_print_snode(out, 0, 0, node, LYS_NOTIF, NULL, 0, &opts);
Michal Vaskoe03bfbb2015-06-16 09:07:49 +02001007 }
1008 }
Radek Krejcid5bf08e2017-01-25 11:35:04 +01001009
1010 /* groupings */
Michal Vasko568b1952018-01-30 15:53:30 +01001011 if ((options & LYS_OUTOPT_TREE_GROUPING) && have_grps) {
1012 ly_print(out, "\n");
Radek Krejcid5bf08e2017-01-25 11:35:04 +01001013 LY_TREE_FOR(data, node) {
1014 if (node->nodetype == LYS_GROUPING) {
Michal Vasko568b1952018-01-30 15:53:30 +01001015 ly_print(out, "%*sgrouping %s:\n", LY_TREE_MOD_DATA_INDENT, "", node->name);
1016
1017 tree_print_snode(out, 0, 0, node, LYS_GROUPING, NULL, 0, &opts);
Radek Krejcid5bf08e2017-01-25 11:35:04 +01001018 }
1019 }
1020 }
1021
Michal Vasko95068c42016-03-24 14:58:11 +01001022 ly_print_flush(out);
Michal Vasko449afde2015-06-04 16:06:49 +02001023
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001024 return EXIT_SUCCESS;
Michal Vasko5ed10f12015-06-04 10:04:57 +02001025}