blob: f8ddaa9b307ef42b9dbca24b203c43fe8d244cea [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
15#include <stdlib.h>
16#include <stdio.h>
17#include <string.h>
Michal Vaskob5c75d72015-06-15 12:16:52 +020018#include <assert.h>
Michal Vasko5ed10f12015-06-04 10:04:57 +020019
Radek Krejci998a0b82015-08-17 13:14:36 +020020#include "common.h"
Radek Krejci76b07902015-10-09 09:11:25 +020021#include "printer.h"
Michal Vasko2d162e12015-09-24 14:33:29 +020022#include "tree_schema.h"
Michal Vasko5ed10f12015-06-04 10:04:57 +020023
Michal Vasko568b1952018-01-30 15:53:30 +010024/* module: <name>
25 * <X>+--rw <node-name> */
26#define LY_TREE_MOD_DATA_INDENT 2
Michal Vasko5ed10f12015-06-04 10:04:57 +020027
Michal Vasko568b1952018-01-30 15:53:30 +010028/* <^>rpcs:
29 * <X>+---x <rpc-name> */
30#define LY_TREE_OP_DATA_INDENT 4
31
32/* +--rw leaf<X>string */
33#define LY_TREE_TYPE_INDENT 3
34
35/* +--rw leaf
36 * | <X>string */
37#define LY_TREE_WRAP_INDENT 2
38
39/* these options are mostly inherited in recursive print, non-recursive options are parameters */
40typedef struct {
41 const struct lys_module *module; /**< (sub)module we are printing from */
42 uint8_t base_indent; /**< base indent size of all the printed text */
43 uint64_t indent; /**< bit-field of sibling (1)/ no sibling(0) on corresponding depths */
44 uint16_t line_length; /**< maximum desired line length */
45 int spec_config; /**< special config flags - 0 (no special config status),
46 1 (read-only - rpc output, notification), 2 (write-only - rpc input) */
47 int options; /**< user-specified tree printer options */
48} tp_opts;
49
50static void tree_print_snode(struct lyout *out, int level, uint16_t max_name_len, const struct lys_node *node, int mask,
51 const struct lys_node *aug_parent, int subtree, tp_opts *opts);
52
53static int
54tree_print_indent(struct lyout *out, int level, tp_opts *opts)
Michal Vaskoe8441232016-09-01 15:32:45 +020055{
Michal Vasko568b1952018-01-30 15:53:30 +010056 int i, ret = 0;
Michal Vaskoe8441232016-09-01 15:32:45 +020057
Michal Vasko568b1952018-01-30 15:53:30 +010058 if (opts->base_indent) {
59 ret += ly_print(out, "%*s", opts->base_indent, " ");
60 }
61 for (i = 0; i < level; ++i) {
62 if (opts->indent & (1 << i)) {
63 ret += ly_print(out, "| ");
Michal Vaskoe8441232016-09-01 15:32:45 +020064 } else {
Michal Vasko568b1952018-01-30 15:53:30 +010065 ret += ly_print(out, " ");
Michal Vaskoe8441232016-09-01 15:32:45 +020066 }
67 }
Michal Vasko568b1952018-01-30 15:53:30 +010068
69 return ret;
Michal Vaskoe8441232016-09-01 15:32:45 +020070}
71
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020072static int
Michal Vasko568b1952018-01-30 15:53:30 +010073tree_sibling_is_valid_child(const struct lys_node *node, int including, const struct lys_module *module,
74 const struct lys_node *aug_parent, LYS_NODE nodetype)
Michal Vasko6db4fce2015-06-08 14:13:49 +020075{
Michal Vasko58416c82017-02-01 11:43:18 +010076 struct lys_node *cur, *cur2;
Michal Vasko7ea0a312015-06-08 10:36:48 +020077
Michal Vasko80630802017-02-15 13:55:57 +010078 assert(!aug_parent || (aug_parent->nodetype == LYS_AUGMENT));
79
Michal Vaskobda37192016-02-15 11:09:13 +010080 if (!node) {
Michal Vasko07898f92015-06-15 12:17:11 +020081 return 0;
82 }
Michal Vasko7ea0a312015-06-08 10:36:48 +020083
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020084 /* has a following printed child */
Radek Krejci1d82ef62015-08-07 14:44:40 +020085 LY_TREE_FOR((struct lys_node *)(including ? node : node->next), cur) {
Michal Vasko80630802017-02-15 13:55:57 +010086 if (aug_parent && (cur->parent != aug_parent)) {
87 /* we are done traversing this augment, the nodes are all direct siblings */
88 return 0;
89 }
90
Michal Vasko568b1952018-01-30 15:53:30 +010091 if (module->type && (lys_main_module(module) != lys_node_module(cur))) {
Michal Vaskobda37192016-02-15 11:09:13 +010092 continue;
93 }
94
Michal Vaskof6f18f42015-09-24 13:06:14 +020095 if (!lys_is_disabled(cur, 0)) {
Michal Vasko32113302017-02-01 11:34:22 +010096 if (cur->nodetype == LYS_USES) {
Michal Vasko568b1952018-01-30 15:53:30 +010097 if (tree_sibling_is_valid_child(cur->child, 1, module, NULL, nodetype)) {
Michal Vaskoc1e89352017-01-25 14:57:55 +010098 return 1;
99 }
Michal Vasko32113302017-02-01 11:34:22 +0100100 } else {
101 switch (nodetype) {
102 case LYS_GROUPING:
Michal Vasko568b1952018-01-30 15:53:30 +0100103 /* we are printing groupings, they are printer separately */
Michal Vasko32113302017-02-01 11:34:22 +0100104 if (cur->nodetype == LYS_GROUPING) {
Michal Vasko568b1952018-01-30 15:53:30 +0100105 return 0;
Michal Vasko32113302017-02-01 11:34:22 +0100106 }
107 break;
108 case LYS_RPC:
109 if (cur->nodetype == LYS_RPC) {
110 return 1;
111 }
112 break;
113 case LYS_NOTIF:
114 if (cur->nodetype == LYS_NOTIF) {
115 return 1;
116 }
117 break;
118 default:
119 if (cur->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_CHOICE
Michal Vasko58416c82017-02-01 11:43:18 +0100120 | LYS_CASE | LYS_ACTION)) {
Michal Vasko32113302017-02-01 11:34:22 +0100121 return 1;
122 }
123 if ((cur->nodetype & (LYS_INPUT | LYS_OUTPUT)) && cur->child) {
124 return 1;
125 }
Michal Vasko58416c82017-02-01 11:43:18 +0100126 /* only nested notifications count here (not top-level) */
127 if (cur->nodetype == LYS_NOTIF) {
128 for (cur2 = lys_parent(cur); cur2 && (cur2->nodetype == LYS_USES); cur2 = lys_parent(cur2));
129 if (cur2) {
130 return 1;
131 }
132 }
Michal Vasko32113302017-02-01 11:34:22 +0100133 break;
Michal Vaskoc1e89352017-01-25 14:57:55 +0100134 }
Michal Vaskof6f18f42015-09-24 13:06:14 +0200135 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200136 }
137 }
Michal Vasko7ea0a312015-06-08 10:36:48 +0200138
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200139 /* if in uses, the following printed child can actually be in the parent node :-/ */
Michal Vaskodcf98e62016-05-05 17:53:53 +0200140 if (lys_parent(node) && (lys_parent(node)->nodetype == LYS_USES)) {
Michal Vasko568b1952018-01-30 15:53:30 +0100141 return tree_sibling_is_valid_child(lys_parent(node), 0, module, NULL, nodetype);
Michal Vasko07898f92015-06-15 12:17:11 +0200142 }
143
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200144 return 0;
Michal Vasko7ea0a312015-06-08 10:36:48 +0200145}
146
Michal Vasko568b1952018-01-30 15:53:30 +0100147static void
148tree_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 +0200149{
Michal Vaskodc300b02017-04-07 14:09:20 +0200150 int next_is_case = 0, has_next = 0;
Michal Vasko14410462015-06-05 15:08:54 +0200151
Michal Vaskoe8441232016-09-01 15:32:45 +0200152 if (level > 64) {
153 LOGINT;
Michal Vasko568b1952018-01-30 15:53:30 +0100154 return;
Michal Vasko253035f2015-12-17 16:58:13 +0100155 }
156
Michal Vasko568b1952018-01-30 15:53:30 +0100157 /* clear level indent (it may have been set for some line wrapping) */
158 opts->indent &= ~(uint64_t)(1 << (level - 1));
Michal Vasko14410462015-06-05 15:08:54 +0200159
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200160 /* this is the direct child of a case */
Michal Vaskodc300b02017-04-07 14:09:20 +0200161 if ((node->nodetype != LYS_CASE) && lys_parent(node) && (lys_parent(node)->nodetype & (LYS_CASE | LYS_CHOICE))) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200162 /* it is not the only child */
Michal Vaskodcf98e62016-05-05 17:53:53 +0200163 if (node->next && lys_parent(node->next) && (lys_parent(node->next)->nodetype == LYS_CHOICE)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200164 next_is_case = 1;
165 }
166 }
Michal Vasko14410462015-06-05 15:08:54 +0200167
Michal Vasko07898f92015-06-15 12:17:11 +0200168 /* next is a node that will actually be printed */
Michal Vasko568b1952018-01-30 15:53:30 +0100169 has_next = tree_sibling_is_valid_child(node, 0, opts->module, aug_parent, node->nodetype);
Michal Vasko14410462015-06-05 15:08:54 +0200170
Michal Vasko568b1952018-01-30 15:53:30 +0100171 /* set level indent */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200172 if (has_next && !next_is_case) {
Michal Vasko568b1952018-01-30 15:53:30 +0100173 opts->indent |= (uint64_t)1 << (level - 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200174 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200175}
176
Michal Vasko568b1952018-01-30 15:53:30 +0100177static uint16_t
178tree_get_max_name_len(const struct lys_node *sibling, const struct lys_node *aug_parent, int type_mask,
179 tp_opts *opts)
Michal Vasko8d479bd2015-06-05 10:50:03 +0200180{
Michal Vasko1e62a092015-12-01 12:27:20 +0100181 const struct lys_node *sub;
Michal Vasko568b1952018-01-30 15:53:30 +0100182 struct lys_module *nodemod;
183 unsigned int max_name_len = 0, name_len;
Michal Vasko8d479bd2015-06-05 10:50:03 +0200184
Michal Vasko568b1952018-01-30 15:53:30 +0100185 LY_TREE_FOR(sibling, sub) {
186 if (opts->module->type && (sub->module != opts->module)) {
Michal Vaskobda37192016-02-15 11:09:13 +0100187 /* when printing submodule, we are only concerned with its own data (they are in the module data) */
188 continue;
189 }
Michal Vasko568b1952018-01-30 15:53:30 +0100190 if (aug_parent && (sub->parent != aug_parent)) {
191 /* when printing augment children, skip other target children */
192 continue;
193 }
194 if (!(sub->nodetype & type_mask)) {
195 /* this sibling will not be printed */
196 continue;
197 }
Michal Vaskobda37192016-02-15 11:09:13 +0100198
Michal Vasko568b1952018-01-30 15:53:30 +0100199 if ((sub->nodetype == LYS_USES) && !(opts->options & LYS_OUTOPT_TREE_USES)) {
200 name_len = tree_get_max_name_len(sub->child, NULL, type_mask, opts);
201 } else {
202 nodemod = lys_node_module(sub);
203 name_len = strlen(sub->name);
204 if (lys_main_module(opts->module) != nodemod) {
205 /* ":" */
206 ++name_len;
207 if (opts->options & LYS_OUTOPT_TREE_RFC) {
208 name_len += strlen(nodemod->prefix);
209 } else {
210 name_len += strlen(nodemod->name);
211 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200212 }
Michal Vasko568b1952018-01-30 15:53:30 +0100213
214 /* add characters for optional opts */
215 switch (sub->nodetype) {
216 case LYS_LEAF:
217 case LYS_LEAFLIST:
218 case LYS_LIST:
219 case LYS_ANYDATA:
220 case LYS_ANYXML:
221 case LYS_CONTAINER:
222 case LYS_CASE:
223 ++name_len;
224 break;
225 case LYS_CHOICE:
226 /* choice is longer :-/ */
227 name_len += 2;
228 if (!(sub->flags & LYS_MAND_TRUE)) {
229 ++name_len;
230 }
231 break;
232 default:
233 break;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200234 }
235 }
Michal Vasko568b1952018-01-30 15:53:30 +0100236
237 if (name_len > max_name_len) {
238 max_name_len = name_len;
239 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200240 }
Michal Vasko8d479bd2015-06-05 10:50:03 +0200241
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200242 return max_name_len;
Michal Vasko8d479bd2015-06-05 10:50:03 +0200243}
244
Michal Vasko568b1952018-01-30 15:53:30 +0100245static int
246tree_leaf_is_mandatory(const struct lys_node *node)
Michal Vasko5ed10f12015-06-04 10:04:57 +0200247{
Michal Vasko568b1952018-01-30 15:53:30 +0100248 const struct lys_node *parent;
Radek Krejcib8048692015-08-05 13:36:34 +0200249 struct lys_node_list *list;
Michal Vasko568b1952018-01-30 15:53:30 +0100250 uint16_t i;
Michal Vasko449afde2015-06-04 16:06:49 +0200251
Michal Vaskodcf98e62016-05-05 17:53:53 +0200252 for (parent = lys_parent(node); parent && parent->nodetype == LYS_USES; parent = lys_parent(parent));
Radek Krejci69372e22015-08-13 09:55:27 +0200253 if (parent && parent->nodetype == LYS_LIST) {
Radek Krejcib8048692015-08-05 13:36:34 +0200254 list = (struct lys_node_list *)parent;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200255 for (i = 0; i < list->keys_size; i++) {
Michal Vasko568b1952018-01-30 15:53:30 +0100256 if (list->keys[i] == (struct lys_node_leaf *)node) {
257 return 1;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200258 }
259 }
260 }
Michal Vasko449afde2015-06-04 16:06:49 +0200261
Michal Vasko568b1952018-01-30 15:53:30 +0100262 return 0;
Michal Vasko5ed10f12015-06-04 10:04:57 +0200263}
264
Michal Vasko568b1952018-01-30 15:53:30 +0100265static int
266tree_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 +0200267{
Michal Vasko568b1952018-01-30 15:53:30 +0100268 if (opts->line_length && (line_printed + indent + len > opts->line_length)) {
269 ly_print(out, "\n");
270 line_printed = tree_print_indent(out, level, opts);
271 /* 3 for config + space */
272 line_printed += ly_print(out, "%*s", 3 + LY_TREE_WRAP_INDENT, "");
273 } else {
274 line_printed += ly_print(out, "%*s", indent, "");
275 }
Michal Vasko449afde2015-06-04 16:06:49 +0200276
Michal Vasko568b1952018-01-30 15:53:30 +0100277 return line_printed;
278}
Michal Vaskob5c75d72015-06-15 12:16:52 +0200279
Michal Vasko568b1952018-01-30 15:53:30 +0100280static int
281tree_print_prefix(struct lyout *out, const struct lys_node *node, tp_opts *opts)
282{
283 uint16_t ret = 0;
284 const struct lys_module *nodemod;
Michal Vaskob5c75d72015-06-15 12:16:52 +0200285
Michal Vaskobda37192016-02-15 11:09:13 +0100286 nodemod = lys_node_module(node);
Michal Vasko568b1952018-01-30 15:53:30 +0100287 if (lys_main_module(opts->module) != nodemod) {
288 if (opts->options & LYS_OUTOPT_TREE_RFC) {
289 ret = ly_print(out, "%s:", nodemod->prefix);
290 } else {
291 ret = ly_print(out, "%s:", nodemod->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200292 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200293 }
Michal Vaskob5c75d72015-06-15 12:16:52 +0200294
Michal Vasko568b1952018-01-30 15:53:30 +0100295 return ret;
296}
Michal Vasko28c6b572015-06-18 12:43:31 +0200297
Michal Vasko568b1952018-01-30 15:53:30 +0100298static int
299tree_print_type(struct lyout *out, const struct lys_type *type, int options, const char **out_str)
300{
301 struct lys_module *type_mod = ((struct lys_tpdf *)type->parent)->module;
302 const char *str;
303 char *tmp;
304 int printed;
Michal Vasko5ed10f12015-06-04 10:04:57 +0200305
Michal Vasko568b1952018-01-30 15:53:30 +0100306 if ((type->base == LY_TYPE_LEAFREF) && !type->der->module) {
307 if (options & LYS_OUTOPT_TREE_NO_LEAFREF) {
308 if (out_str) {
309 printed = 7;
310 *out_str = lydict_insert(type_mod->ctx, "leafref", printed);
311 } else {
312 printed = ly_print(out, "leafref");
313 }
314 } else {
315 if (options & LYS_OUTOPT_TREE_RFC) {
316 str = transform_json2schema(type_mod, type->info.lref.path);
317 if (out_str) {
318 printed = 3 + strlen(str);
319 tmp = malloc(printed + 1);
320 LY_CHECK_ERR_RETURN(!tmp, LOGMEM, 0);
321 sprintf(tmp, "-> %s", str);
322 *out_str = lydict_insert_zc(type_mod->ctx, tmp);
323 } else {
324 printed = ly_print(out, "-> %s", str);
325 }
326 lydict_remove(type_mod->ctx, str);
327 } else {
328 if (out_str) {
329 printed = 3 + strlen(type->info.lref.path);
330 tmp = malloc(printed + 1);
331 LY_CHECK_ERR_RETURN(!tmp, LOGMEM, 0);
332 sprintf(tmp, "-> %s", type->info.lref.path);
333 *out_str = lydict_insert_zc(type_mod->ctx, tmp);
334 } else {
335 printed = ly_print(out, "-> %s", type->info.lref.path);
336 }
337 }
Michal Vasko3510f892016-02-15 14:33:52 +0100338 }
Michal Vasko568b1952018-01-30 15:53:30 +0100339 } else if (!lys_type_is_local(type)) {
340 if (options & LYS_OUTOPT_TREE_RFC) {
341 str = transform_module_name2import_prefix(type_mod, type->der->module->name);
342 if (out_str) {
343 printed = strlen(str) + 1 + strlen(type->der->name);
344 tmp = malloc(printed + 1);
345 LY_CHECK_ERR_RETURN(!tmp, LOGMEM, 0);
346 sprintf(tmp, "%s:%s", str, type->der->name);
347 *out_str = lydict_insert_zc(type_mod->ctx, tmp);
348 } else {
349 printed = ly_print(out, "%s:%s", str, type->der->name);
350 }
351 } else {
352 if (out_str) {
353 printed = strlen(type->der->module->name) + 1 + strlen(type->der->name);
354 tmp = malloc(printed + 1);
355 LY_CHECK_ERR_RETURN(!tmp, LOGMEM, 0);
356 sprintf(tmp, "%s:%s", type->der->module->name, type->der->name);
357 *out_str = lydict_insert_zc(type_mod->ctx, tmp);
358 } else {
359 printed = ly_print(out, "%s:%s", type->der->module->name, type->der->name);
360 }
361 }
362 } else {
363 if (out_str) {
364 printed = strlen(type->der->name);
365 *out_str = lydict_insert(type_mod->ctx, type->der->name, printed);
366 } else {
367 printed = ly_print(out, "%s", type->der->name);
368 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200369 }
Michal Vasko568b1952018-01-30 15:53:30 +0100370
371 return printed;
Michal Vasko5ed10f12015-06-04 10:04:57 +0200372}
373
Michal Vasko568b1952018-01-30 15:53:30 +0100374static int
375tree_print_config(struct lyout *out, const struct lys_node *node, int spec_config)
Michal Vasko5ed10f12015-06-04 10:04:57 +0200376{
Michal Vasko568b1952018-01-30 15:53:30 +0100377 int ret;
Radek Krejci7e97c352015-06-19 16:26:34 +0200378
Michal Vasko568b1952018-01-30 15:53:30 +0100379 switch (node->nodetype) {
380 case LYS_RPC:
381 case LYS_ACTION:
382 return ly_print(out, "-x ");
383 case LYS_NOTIF:
384 return ly_print(out, "-n ");
385 case LYS_USES:
386 return ly_print(out, "-u ");
387 case LYS_CASE:
388 return ly_print(out, ":(");
389 default:
390 break;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200391 }
Michal Vasko568b1952018-01-30 15:53:30 +0100392
393 if (spec_config == 0) {
394 ret = ly_print(out, "%s ", (node->flags & LYS_CONFIG_W) ? "rw" : (node->flags & LYS_CONFIG_R) ? "ro" : "--");
395 } else if (spec_config == 1) {
396 ret = ly_print(out, "-w ");
397 } else if (spec_config == 2) {
398 ret = ly_print(out, "ro ");
399 }
400
401 if (node->nodetype == LYS_CHOICE) {
402 ret += ly_print(out, "(");
403 }
404 return ret;
Michal Vasko5ed10f12015-06-04 10:04:57 +0200405}
406
Michal Vasko568b1952018-01-30 15:53:30 +0100407static int
408tree_print_features(struct lyout *out, struct lys_iffeature *iff1, uint8_t iff1_size, struct lys_iffeature *iff2,
409 uint8_t iff2_size, tp_opts *opts, const char **out_str)
Michal Vaskob5c75d72015-06-15 12:16:52 +0200410{
Michal Vasko568b1952018-01-30 15:53:30 +0100411 int i, printed;
412 struct lyout *o;
Radek Krejci7e97c352015-06-19 16:26:34 +0200413
Michal Vasko568b1952018-01-30 15:53:30 +0100414 if (!iff1_size && !iff2_size) {
415 return 0;
416 }
417
418 if (out_str) {
419 o = malloc(sizeof *o);
420 LY_CHECK_ERR_RETURN(!o, LOGMEM, 0);
421 o->type = LYOUT_MEMORY;
422 o->method.mem.buf = NULL;
423 o->method.mem.len = 0;
424 o->method.mem.size = 0;
425 } else {
426 o = out;
427 }
428
429 printed = ly_print(o, "{");
430 for (i = 0; i < iff1_size; i++) {
431 if (i > 0) {
432 printed += ly_print(o, ",");
433 }
434 printed += ly_print_iffeature(o, opts->module, &iff1[i], opts->options & LYS_OUTOPT_TREE_RFC ? 2 : 1);
435 }
436 for (i = 0; i < iff2_size; i++) {
437 if (i > 0) {
438 printed += ly_print(o, ",");
439 }
440 printed += ly_print_iffeature(o, opts->module, &iff2[i], opts->options & LYS_OUTOPT_TREE_RFC ? 2 : 1);
441 }
442 printed += ly_print(o, "}?");
443
444 if (out_str) {
445 *out_str = lydict_insert_zc(opts->module->ctx, o->method.mem.buf);
446 free(o);
447 }
448
449 return printed;
450}
451
452static int
453tree_print_keys(struct lyout *out, struct lys_node_leaf **keys, uint8_t keys_size, tp_opts *opts, const char **out_str)
454{
455 int i, printed;
456 struct lyout *o;
457
458 if (!keys_size) {
459 return 0;
460 }
461
462 if (out_str) {
463 o = malloc(sizeof *o);
464 LY_CHECK_ERR_RETURN(!o, LOGMEM, 0);
465 o->type = LYOUT_MEMORY;
466 o->method.mem.buf = NULL;
467 o->method.mem.len = 0;
468 o->method.mem.size = 0;
469 } else {
470 o = out;
471 }
472
473 printed = ly_print(o, "[");
474 for (i = 0; i < keys_size; i++) {
475 printed += ly_print(o, "%s%s", keys[i]->name, i + 1 < keys_size ? " " : "]");
476 }
477
478 if (out_str) {
479 *out_str = lydict_insert_zc(opts->module->ctx, o->method.mem.buf);
480 free(o);
481 }
482
483 return printed;
484}
485
486/**
487 * @brief Print schema node in YANG tree diagram formatting.
488 *
489 * @param[in] out libyang output.
490 * @param[in] level Current level of depth.
491 * @param[in] max_name_len Maximal name length of all the siblings (relevant only for nodes with type).
492 * @param[in] node Schema node to print.
493 * @param[in] mask Type mask of children nodes to be printed.
494 * @param[in] aug_parent Augment node parent in case we are printing its direct children.
495 * @param[in] opts Tree printer options structure.
496 */
497static void
498tree_print_snode(struct lyout *out, int level, uint16_t max_name_len, const struct lys_node *node, int mask,
499 const struct lys_node *aug_parent, int subtree, tp_opts *opts)
500{
501 struct lys_node *sub;
502 int line_len, node_len, child_mask;
503 uint8_t text_len, text_indent;
504 uint16_t max_child_len;
505 const char *text_str;
506
507 /* disabled/not printed node */
508 if (lys_is_disabled(node, (node->parent && node->parent->nodetype == LYS_AUGMENT) ? 1 : 0) || !(node->nodetype & mask)) {
Michal Vaskoefbb3192015-07-08 10:35:00 +0200509 return;
Radek Krejci7e97c352015-06-19 16:26:34 +0200510 }
Michal Vaskob5c75d72015-06-15 12:16:52 +0200511
Michal Vasko568b1952018-01-30 15:53:30 +0100512 /* implicit input/output */
513 if (((node->nodetype & mask) & (LYS_INPUT | LYS_OUTPUT)) && (node->flags & LYS_IMPLICIT)) {
Michal Vaskoefbb3192015-07-08 10:35:00 +0200514 return;
Radek Krejci7e97c352015-06-19 16:26:34 +0200515 }
Michal Vaskoe03bfbb2015-06-16 09:07:49 +0200516
Michal Vasko568b1952018-01-30 15:53:30 +0100517 /* special uses and grouping handling */
Radek Krejci1d82ef62015-08-07 14:44:40 +0200518 switch (node->nodetype & mask) {
Michal Vasko568b1952018-01-30 15:53:30 +0100519 case LYS_USES:
520 if (opts->options & LYS_OUTOPT_TREE_USES) {
521 break;
522 }
523 /* fallthrough */
524 case LYS_GROUPING:
525 goto print_children;
526 default:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200527 break;
Michal Vasko568b1952018-01-30 15:53:30 +0100528 }
529
530 /* print indent */
531 line_len = tree_print_indent(out, level, opts);
532 /* print status */
533 line_len += ly_print(out, "%s--", (node->flags & LYS_STATUS_DEPRC ? "x" : (node->flags & LYS_STATUS_OBSLT ? "o" : "+")));
534 /* print config flags (or special opening for case, choice) */
535 line_len += tree_print_config(out, node, opts->spec_config);
536 /* print optionally prefix */
537 node_len = tree_print_prefix(out, node, opts);
538 /* print name */
539 node_len += ly_print(out, node->name);
540
541 /* print one-character opts */
542 switch (node->nodetype & mask) {
Radek Krejci76512572015-08-04 09:47:08 +0200543 case LYS_LEAF:
Michal Vasko568b1952018-01-30 15:53:30 +0100544 if (!(node->flags & LYS_MAND_TRUE) && !tree_leaf_is_mandatory(node)) {
545 node_len += ly_print(out, "?");
546 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200547 break;
Michal Vasko568b1952018-01-30 15:53:30 +0100548 case LYS_ANYDATA:
549 case LYS_ANYXML:
550 if (!(node->flags & LYS_MAND_TRUE)) {
551 node_len += ly_print(out, "?");
552 }
553 break;
554 case LYS_CONTAINER:
555 if (((struct lys_node_container *)node)->presence) {
556 node_len += ly_print(out, "!");
557 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200558 break;
Radek Krejci76512572015-08-04 09:47:08 +0200559 case LYS_LIST:
Michal Vasko568b1952018-01-30 15:53:30 +0100560 case LYS_LEAFLIST:
561 node_len += ly_print(out, "*");
Michal Vaskoca7cbc42016-07-01 11:36:53 +0200562 break;
Michal Vaskod4742e62016-02-15 15:11:55 +0100563 case LYS_CASE:
Michal Vasko568b1952018-01-30 15:53:30 +0100564 /* kinda shady, but consistent in a way */
565 node_len += ly_print(out, ")");
566 break;
567 case LYS_CHOICE:
568 node_len += ly_print(out, ")");
569 if (!(node->flags & LYS_MAND_TRUE)) {
570 node_len += ly_print(out, "?");
571 }
572 break;
573 default:
574 break;
575 }
576 line_len += node_len;
577
578 /**
579 * wrapped print
580 */
581
582 /* learn next level indent (there is never a sibling for subtree) */
583 ++level;
584 if (!subtree) {
585 tree_next_indent(level, node, aug_parent, opts);
586 }
587
588 /* print type/keys */
589 switch (node->nodetype & mask) {
590 case LYS_LEAF:
591 case LYS_LEAFLIST:
592 assert(max_name_len);
593 text_indent = LY_TREE_TYPE_INDENT + (uint8_t)(max_name_len - node_len);
594 text_len = tree_print_type(out, &((struct lys_node_leaf *)node)->type, opts->options, &text_str);
595 line_len = tree_print_wrap(out, level, line_len, text_indent, text_len, opts);
596 line_len += ly_print(out, text_str);
597 lydict_remove(opts->module->ctx, text_str);
598 break;
599 case LYS_ANYDATA:
600 assert(max_name_len);
601 text_indent = LY_TREE_TYPE_INDENT + (uint8_t)(max_name_len - node_len);
602 line_len = tree_print_wrap(out, level, line_len, text_indent, 7, opts);
603 line_len += ly_print(out, "anydata");
604 break;
605 case LYS_ANYXML:
606 assert(max_name_len);
607 text_indent = LY_TREE_TYPE_INDENT + (uint8_t)(max_name_len - node_len);
608 line_len = tree_print_wrap(out, level, line_len, text_indent, 6, opts);
609 line_len += ly_print(out, "anyxml");
610 break;
611 case LYS_LIST:
612 text_len = tree_print_keys(out, ((struct lys_node_list *)node)->keys, ((struct lys_node_list *)node)->keys_size,
613 opts, &text_str);
614 if (text_len) {
615 line_len = tree_print_wrap(out, level, line_len, 1, text_len, opts);
616 line_len += ly_print(out, text_str);
617 lydict_remove(opts->module->ctx, text_str);
618 }
619 break;
620 default:
621 break;
622 }
623
624 /* print default */
625 if (!(opts->options & LYS_OUTOPT_TREE_RFC)) {
626 switch (node->nodetype & mask) {
627 case LYS_LEAF:
628 text_str = ((struct lys_node_leaf *)node)->dflt;
629 if (text_str) {
630 line_len = tree_print_wrap(out, level, line_len, 1, 2 + strlen(text_str), opts);
631 line_len += ly_print(out, "<%s>", text_str);
632 }
633 break;
634 case LYS_CHOICE:
635 sub = ((struct lys_node_choice *)node)->dflt;
636 if (sub) {
637 line_len = tree_print_wrap(out, level, line_len, 1, 2 + strlen(sub->name), opts);
638 line_len += ly_print(out, "<%s>", sub->name);
639 }
640 break;
641 default:
642 break;
643 }
644 }
645
646 /* print if-features */
647 switch (node->nodetype & mask) {
648 case LYS_CONTAINER:
649 case LYS_LIST:
650 case LYS_CHOICE:
651 case LYS_CASE:
652 case LYS_ANYDATA:
653 case LYS_ANYXML:
654 case LYS_LEAF:
655 case LYS_LEAFLIST:
656 case LYS_RPC:
657 case LYS_ACTION:
658 case LYS_NOTIF:
659 case LYS_USES:
660 if (node->parent && (node->parent->nodetype == LYS_AUGMENT)) {
661 /* if-features from an augment are de facto inherited */
662 text_len = tree_print_features(out, node->iffeature, node->iffeature_size,
663 node->parent->iffeature, node->parent->iffeature_size, opts, &text_str);
664 } else {
665 text_len = tree_print_features(out, node->iffeature, node->iffeature_size, NULL, 0, opts, &text_str);
666 }
667 if (text_len) {
668 line_len = tree_print_wrap(out, level, line_len, 1, text_len, opts);
669 line_len += ly_print(out, text_str);
670 lydict_remove(opts->module->ctx, text_str);
671 }
672 break;
673 default:
674 /* only grouping */
675 break;
676 }
677
678 /* this node is finished printing */
679 ly_print(out, "\n");
680
681 if ((subtree == 1) || ((node->nodetype & mask) == LYS_USES)) {
682 /* we are printing subtree parents, finish here (or uses option) */
683 return;
684 }
685
686 /* set special config flag */
687 switch (node->nodetype & mask) {
688 case LYS_INPUT:
689 opts->spec_config = 1;
690 break;
691 case LYS_OUTPUT:
692 case LYS_NOTIF:
693 opts->spec_config = 2;
694 break;
695 default:
696 break;
697 }
698
699print_children:
700 /* set child mask and learn the longest child name (needed only if a child can have type) */
701 switch (node->nodetype & mask) {
702 case LYS_LEAF:
703 case LYS_LEAFLIST:
704 case LYS_ANYDATA:
705 case LYS_ANYXML:
706 child_mask = 0;
707 max_child_len = 0;
708 break;
709 case LYS_RPC:
710 case LYS_ACTION:
711 child_mask = LYS_INPUT | LYS_OUTPUT;
712 max_child_len = 0;
713 break;
714 case LYS_CHOICE:
715 child_mask = LYS_CASE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA;
716 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
717 break;
718 case LYS_CASE:
719 case LYS_NOTIF:
720 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES;
721 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
722 break;
723 case LYS_INPUT:
724 case LYS_OUTPUT:
725 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES;
726 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
727 break;
728 case LYS_USES:
729 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES | LYS_ACTION | LYS_NOTIF;
730 /* inherit the name length from the parent, it does not change */
731 max_child_len = max_name_len;
732 break;
733 case LYS_CONTAINER:
734 case LYS_LIST:
735 case LYS_GROUPING:
736 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES | LYS_ACTION | LYS_NOTIF;
737 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
738 break;
739 }
740
741 /* print descendants (children) */
742 if (child_mask) {
743 LY_TREE_FOR(node->child, sub) {
744 /* submodule, foreign augments */
745 if (opts->module->type && (sub->parent != node) && (sub->module != opts->module)) {
746 continue;
747 }
748 tree_print_snode(out, level, max_child_len, sub, child_mask, NULL, 0, opts);
749 }
750 }
751
752 /* reset special config flag */
753 switch (node->nodetype & mask) {
754 case LYS_INPUT:
755 case LYS_OUTPUT:
756 case LYS_NOTIF:
757 opts->spec_config = 0;
Michal Vaskod4742e62016-02-15 15:11:55 +0100758 break;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200759 default:
760 break;
761 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200762}
763
Michal Vasko568b1952018-01-30 15:53:30 +0100764static void
765tree_print_subtree(struct lyout *out, const struct lys_node *node, tp_opts *opts)
766{
767 unsigned int depth, i, j;
768 int level = 0;
769 const struct lys_node *parent;
770
771 /* learn the depth of the node */
772 depth = 0;
773 parent = node;
774 while (lys_parent(parent)) {
775 if (lys_parent(parent)->nodetype != LYS_USES) {
776 ++depth;
777 }
778 parent = lys_parent(parent);
779 }
780
781 if (parent->nodetype == LYS_RPC) {
782 ly_print(out, "\n%*srpcs:\n", LY_TREE_MOD_DATA_INDENT, "");
783 opts->base_indent = LY_TREE_OP_DATA_INDENT;
784 } else if (parent->nodetype == LYS_NOTIF) {
785 ly_print(out, "\n%*snotifications:\n", LY_TREE_MOD_DATA_INDENT, "");
786 opts->base_indent = LY_TREE_OP_DATA_INDENT;
787 }
788
789 /* print all the parents */
790 if (depth) {
791 i = depth;
792 do {
793 parent = node;
794 for (j = 0; j < i; ++j) {
795 do {
796 parent = lys_parent(parent);
797 } while (parent->nodetype == LYS_USES);
798 }
799
800 tree_print_snode(out, level, 0, parent, LYS_CONTAINER | LYS_LIST | LYS_NOTIF | LYS_RPC | LYS_ACTION
801 | LYS_INPUT | LYS_OUTPUT, NULL, 1, opts);
802
803 ++level;
804 --i;
805 } while (i);
806 }
807
808 /* print the node and its descendants */
809 tree_print_snode(out, level, 0, node, LYS_ANY, NULL, 2, opts);
810}
811
812static int
813tree_print_aug_target(struct lyout *out, int line_printed, uint8_t indent, const char *path, tp_opts *opts)
814{
815 int printed, is_last, len;
816 const char *cur, *next;
817
818 printed = line_printed;
819 cur = path;
820 do {
821 next = strchr(cur + 1, '/');
822 if (!next) {
823 len = strlen(cur) + 1;
824 is_last = 1;
825 } else {
826 len = next - cur;
827 is_last = 0;
828 }
829
830 if (opts->line_length && cur != path && (printed + len > opts->line_length)) {
831 /* line_printed is treated as the base indent */
832 printed = ly_print(out, "\n%*s", line_printed + indent, "");
833 /* minus the newline */
834 --printed;
835 }
836 printed += ly_print(out, "%.*s%s", len, cur, is_last ? ":" : "");
837
838 cur = next;
839 } while (!is_last);
840
841 return printed;
842}
843
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200844int
Michal Vasko568b1952018-01-30 15:53:30 +0100845tree_print_model(struct lyout *out, const struct lys_module *module, const char *target_schema_path,
846 int ll, int options)
Michal Vasko5ed10f12015-06-04 10:04:57 +0200847{
Michal Vaskobda37192016-02-15 11:09:13 +0100848 struct lys_node *node, *data;
Michal Vasko568b1952018-01-30 15:53:30 +0100849 struct ly_set *set;
850 uint16_t max_child_len;
851 int have_rpcs = 0, have_notifs = 0, have_grps = 0, have_augs = 0, printed;
852 const char *str;
853 int i, mask;
854 tp_opts opts;
855
856 memset(&opts, 0, sizeof opts);
857 opts.module = module;
858 opts.line_length = ll;
859 opts.options = options;
860
861 /* we are printing only a subtree */
862 if (target_schema_path) {
863 set = lys_find_path(module, NULL, target_schema_path);
864 if (!set) {
865 return EXIT_FAILURE;
866 } else if (set->number != 1) {
867 LOGVAL(LYE_PATH_INNODE, LY_VLOG_NONE, NULL);
868 if (set->number == 0) {
869 LOGVAL(LYE_SPEC, LY_VLOG_PREV, NULL, "Schema path \"%s\" did not match any nodes.", target_schema_path);
870 } else {
871 LOGVAL(LYE_SPEC, LY_VLOG_PREV, NULL, "Schema path \"%s\" matched more nodes.", target_schema_path);
872 }
873 ly_set_free(set);
874 return EXIT_FAILURE;
875 }
876
877 node = set->set.s[0];
878 ly_set_free(set);
879 }
Michal Vasko253035f2015-12-17 16:58:13 +0100880
Michal Vaskofc6ac172015-07-07 09:46:46 +0200881 if (module->type) {
Michal Vasko568b1952018-01-30 15:53:30 +0100882 ly_print(out, "submodule: %s", module->name);
Michal Vaskobda37192016-02-15 11:09:13 +0100883 data = ((struct lys_submodule *)module)->belongsto->data;
Michal Vasko568b1952018-01-30 15:53:30 +0100884 if (options & LYS_OUTOPT_TREE_RFC) {
885 ly_print(out, "\n");
886 } else {
887 ly_print(out, " (belongs-to %s)\n", ((struct lys_submodule *)module)->belongsto->name);
888 }
Michal Vaskofc6ac172015-07-07 09:46:46 +0200889 } else {
Radek Krejci76b07902015-10-09 09:11:25 +0200890 ly_print(out, "module: %s\n", module->name);
Michal Vaskobda37192016-02-15 11:09:13 +0100891 data = module->data;
Michal Vaskofc6ac172015-07-07 09:46:46 +0200892 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200893
Michal Vasko568b1952018-01-30 15:53:30 +0100894 /* only subtree */
895 if (target_schema_path) {
896 opts.base_indent = LY_TREE_MOD_DATA_INDENT;
897 tree_print_subtree(out, node, &opts);
898 return EXIT_SUCCESS;
899 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200900
Michal Vasko568b1952018-01-30 15:53:30 +0100901 /* module */
902 opts.base_indent = LY_TREE_MOD_DATA_INDENT;
903 mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES;
904 max_child_len = tree_get_max_name_len(data, NULL, mask, &opts);
Michal Vaskobda37192016-02-15 11:09:13 +0100905 LY_TREE_FOR(data, node) {
Michal Vasko568b1952018-01-30 15:53:30 +0100906 if (opts.module->type && (node->module != opts.module)) {
Michal Vaskobda37192016-02-15 11:09:13 +0100907 /* we're printing the submodule only */
908 continue;
909 }
910
Michal Vasko568b1952018-01-30 15:53:30 +0100911 switch (node->nodetype) {
Radek Krejci92720552015-10-05 15:28:27 +0200912 case LYS_RPC:
Radek Krejciadf7a8e2015-12-10 13:11:17 +0100913 if (!lys_is_disabled(node, 0)) {
914 have_rpcs++;
915 }
Radek Krejci92720552015-10-05 15:28:27 +0200916 break;
917 case LYS_NOTIF:
Radek Krejciadf7a8e2015-12-10 13:11:17 +0100918 if (!lys_is_disabled(node, 0)) {
919 have_notifs++;
920 }
Radek Krejci92720552015-10-05 15:28:27 +0200921 break;
Radek Krejcid5bf08e2017-01-25 11:35:04 +0100922 case LYS_GROUPING:
Michal Vasko568b1952018-01-30 15:53:30 +0100923 if ((options & LYS_OUTOPT_TREE_GROUPING) && !lys_is_disabled(node, 0)) {
Radek Krejcid5bf08e2017-01-25 11:35:04 +0100924 have_grps++;
925 }
926 break;
Radek Krejci92720552015-10-05 15:28:27 +0200927 default:
Michal Vasko568b1952018-01-30 15:53:30 +0100928 tree_print_snode(out, 0, max_child_len, node, mask, NULL, 0, &opts);
Radek Krejci92720552015-10-05 15:28:27 +0200929 break;
930 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200931 }
Michal Vasko5ed10f12015-06-04 10:04:57 +0200932
Michal Vasko568b1952018-01-30 15:53:30 +0100933 /* all remaining nodes printed with operation indent */
934 opts.base_indent = LY_TREE_OP_DATA_INDENT;
935
936 /* augments */
Radek Krejcid49202f2016-02-11 12:53:39 +0100937 for (i = 0; i < module->augment_size; i++) {
Michal Vasko861ee6d2016-02-15 15:11:31 +0100938 if ((module->type && (module->augment[i].target->module == module))
Michal Vasko9089c8d2016-10-25 09:23:53 +0200939 || (!module->type && (lys_node_module(module->augment[i].target) == module))
940 || lys_is_disabled((struct lys_node *)&module->augment[i], 0)) {
Michal Vaskoe79abe82016-02-15 12:33:23 +0100941 /* submodule, target is our submodule or module, target is in our module or any submodules */
942 continue;
943 }
944
Michal Vasko568b1952018-01-30 15:53:30 +0100945 if (!have_augs) {
946 ly_print(out, "\n");
947 have_augs = 1;
948 }
949
950 printed = ly_print(out, "%*saugment ", LY_TREE_MOD_DATA_INDENT, "");
951 if (options & LYS_OUTOPT_TREE_RFC) {
952 str = transform_json2schema(module, module->augment[i].target_name);
953 tree_print_aug_target(out, printed, LY_TREE_WRAP_INDENT, str, &opts);
954 lydict_remove(module->ctx, str);
955 } else {
956 tree_print_aug_target(out, printed, LY_TREE_WRAP_INDENT, module->augment[i].target_name, &opts);
957 }
958 ly_print(out, "\n");
959
960 data = (struct lys_node *)&module->augment[i];
961 mask = LYS_CHOICE | LYS_CASE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES
962 | LYS_ACTION | LYS_NOTIF;
963 max_child_len = tree_get_max_name_len(data->child, data, mask, &opts);
964 LY_TREE_FOR(data->child, node) {
Michal Vaskod4742e62016-02-15 15:11:55 +0100965 /* submodule, foreign augments */
Michal Vasko568b1952018-01-30 15:53:30 +0100966 if (node->parent != data) {
Michal Vaskod4742e62016-02-15 15:11:55 +0100967 continue;
968 }
Michal Vasko568b1952018-01-30 15:53:30 +0100969 tree_print_snode(out, 0, max_child_len, node, mask, data, 0, &opts);
Radek Krejcid49202f2016-02-11 12:53:39 +0100970 }
971 }
972
Michal Vasko568b1952018-01-30 15:53:30 +0100973 /* rpcs */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200974 if (have_rpcs) {
Michal Vasko568b1952018-01-30 15:53:30 +0100975 ly_print(out, "\n%*srpcs:\n", LY_TREE_MOD_DATA_INDENT, "");
976
Michal Vaskobda37192016-02-15 11:09:13 +0100977 LY_TREE_FOR(data, node) {
Michal Vasko568b1952018-01-30 15:53:30 +0100978 tree_print_snode(out, 0, 0, node, LYS_RPC, NULL, 0, &opts);
Michal Vaskob5c75d72015-06-15 12:16:52 +0200979 }
980 }
Michal Vasko449afde2015-06-04 16:06:49 +0200981
Michal Vasko568b1952018-01-30 15:53:30 +0100982 /* notifications */
Michal Vaskoe03bfbb2015-06-16 09:07:49 +0200983 if (have_notifs) {
Michal Vasko568b1952018-01-30 15:53:30 +0100984 ly_print(out, "\n%*snotifications:\n", LY_TREE_MOD_DATA_INDENT, "");
985
Michal Vaskobda37192016-02-15 11:09:13 +0100986 LY_TREE_FOR(data, node) {
Michal Vasko568b1952018-01-30 15:53:30 +0100987 tree_print_snode(out, 0, 0, node, LYS_NOTIF, NULL, 0, &opts);
Michal Vaskoe03bfbb2015-06-16 09:07:49 +0200988 }
989 }
Radek Krejcid5bf08e2017-01-25 11:35:04 +0100990
991 /* groupings */
Michal Vasko568b1952018-01-30 15:53:30 +0100992 if ((options & LYS_OUTOPT_TREE_GROUPING) && have_grps) {
993 ly_print(out, "\n");
Radek Krejcid5bf08e2017-01-25 11:35:04 +0100994 LY_TREE_FOR(data, node) {
995 if (node->nodetype == LYS_GROUPING) {
Michal Vasko568b1952018-01-30 15:53:30 +0100996 ly_print(out, "%*sgrouping %s:\n", LY_TREE_MOD_DATA_INDENT, "", node->name);
997
998 tree_print_snode(out, 0, 0, node, LYS_GROUPING, NULL, 0, &opts);
Radek Krejcid5bf08e2017-01-25 11:35:04 +0100999 }
1000 }
1001 }
1002
Michal Vasko95068c42016-03-24 14:58:11 +01001003 ly_print_flush(out);
Michal Vasko449afde2015-06-04 16:06:49 +02001004
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001005 return EXIT_SUCCESS;
Michal Vasko5ed10f12015-06-04 10:04:57 +02001006}