1 /**
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 *
8 * 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
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
15 #include <stdint.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <assert.h>
20
21 #include "common.h"
22 #include "printer.h"
23 #include "tree_schema.h"
24
25 /* module: <name>
26 * <X>+--rw <node-name> */
27 #define LY_TREE_MOD_DATA_INDENT 2
28
29 /* <^>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 */
41 typedef 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
51 static 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
54 static int
tree_print_indent(struct lyout * out,int level,tp_opts * opts)55 tree_print_indent(struct lyout *out, int level, tp_opts *opts)
56 {
57 int i, ret = 0;
58
59 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, "| ");
65 } else {
66 ret += ly_print(out, " ");
67 }
68 }
69
70 return ret;
71 }
72
73 static int
tree_sibling_is_valid_child(const struct lys_node * node,int including,const struct lys_module * module,const struct lys_node * aug_parent,LYS_NODE nodetype)74 tree_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)
76 {
77 struct lys_node *cur, *cur2;
78
79 assert(!aug_parent || (aug_parent->nodetype == LYS_AUGMENT));
80
81 if (!node) {
82 return 0;
83 } 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;
86 }
87
88 /* has a following printed child */
89 LY_TREE_FOR((struct lys_node *)(including ? node : node->next), cur) {
90 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
95 if (module->type && (lys_main_module(module) != lys_node_module(cur))) {
96 continue;
97 }
98
99 if (!lys_is_disabled(cur, 0)) {
100 if ((cur->nodetype == LYS_USES) || ((cur->nodetype == LYS_CASE) && (cur->flags & LYS_IMPLICIT))) {
101 if (tree_sibling_is_valid_child(cur->child, 1, module, NULL, nodetype)) {
102 return 1;
103 }
104 } else {
105 switch (nodetype) {
106 case LYS_GROUPING:
107 /* we are printing groupings, they are printed separately */
108 if (cur->nodetype == LYS_GROUPING) {
109 return 0;
110 }
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
124 | LYS_CASE | LYS_ACTION)) {
125 return 1;
126 }
127 if ((cur->nodetype & (LYS_INPUT | LYS_OUTPUT)) && cur->child) {
128 return 1;
129 }
130 /* 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 }
137 break;
138 }
139 }
140 }
141 }
142
143 /* if in uses, the following printed child can actually be in the parent node :-/ */
144 if (lys_parent(node) && (lys_parent(node)->nodetype == LYS_USES)) {
145 return tree_sibling_is_valid_child(lys_parent(node), 0, module, NULL, nodetype);
146 }
147
148 return 0;
149 }
150
151 static void
tree_next_indent(int level,const struct lys_node * node,const struct lys_node * aug_parent,tp_opts * opts)152 tree_next_indent(int level, const struct lys_node *node, const struct lys_node *aug_parent, tp_opts *opts)
153 {
154 int next_is_case = 0, has_next = 0;
155
156 if (level > 64) {
157 LOGINT(node->module->ctx);
158 return;
159 }
160
161 /* clear level indent (it may have been set for some line wrapping) */
162 opts->indent &= ~(uint64_t)(1ULL << (level - 1));
163
164 /* this is the direct child of a case */
165 if ((node->nodetype != LYS_CASE) && lys_parent(node) && (lys_parent(node)->nodetype & (LYS_CASE | LYS_CHOICE))) {
166 /* it is not the only child */
167 if (node->next && lys_parent(node->next) && (lys_parent(node->next)->nodetype == LYS_CHOICE)) {
168 next_is_case = 1;
169 }
170 }
171
172 /* next is a node that will actually be printed */
173 has_next = tree_sibling_is_valid_child(node, 0, opts->module, aug_parent, node->nodetype);
174
175 /* set level indent */
176 if (has_next && !next_is_case) {
177 opts->indent |= (uint64_t)1ULL << (level - 1);
178 }
179 }
180
181 static uint16_t
tree_get_max_name_len(const struct lys_node * sibling,const struct lys_node * aug_parent,int type_mask,tp_opts * opts)182 tree_get_max_name_len(const struct lys_node *sibling, const struct lys_node *aug_parent, int type_mask,
183 tp_opts *opts)
184 {
185 const struct lys_node *sub;
186 struct lys_module *nodemod;
187 unsigned int max_name_len = 0, name_len;
188
189 LY_TREE_FOR(sibling, sub) {
190 if (opts->module->type && (sub->module != opts->module)) {
191 /* when printing submodule, we are only concerned with its own data (they are in the module data) */
192 continue;
193 }
194 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 }
202
203 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 }
216 }
217
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;
238 }
239 }
240
241 if (name_len > max_name_len) {
242 max_name_len = name_len;
243 }
244 }
245
246 return max_name_len;
247 }
248
249 static int
tree_leaf_is_mandatory(const struct lys_node * node)250 tree_leaf_is_mandatory(const struct lys_node *node)
251 {
252 const struct lys_node *parent;
253 struct lys_node_list *list;
254 uint16_t i;
255
256 for (parent = lys_parent(node); parent && parent->nodetype == LYS_USES; parent = lys_parent(parent));
257 if (parent && parent->nodetype == LYS_LIST) {
258 list = (struct lys_node_list *)parent;
259 for (i = 0; i < list->keys_size; i++) {
260 if (list->keys[i] == (struct lys_node_leaf *)node) {
261 return 1;
262 }
263 }
264 }
265
266 return 0;
267 }
268
269 static int
tree_print_wrap(struct lyout * out,int level,int line_printed,uint8_t indent,uint16_t len,tp_opts * opts)270 tree_print_wrap(struct lyout *out, int level, int line_printed, uint8_t indent, uint16_t len, tp_opts *opts)
271 {
272 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 }
280
281 return line_printed;
282 }
283
284 static int
tree_print_prefix(struct lyout * out,const struct lys_node * node,tp_opts * opts)285 tree_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;
289
290 nodemod = lys_node_module(node);
291 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);
296 }
297 }
298
299 return ret;
300 }
301
302 static int
tree_print_type(struct lyout * out,const struct lys_type * type,int options,const char ** out_str)303 tree_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;
309
310 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);
324 LY_CHECK_ERR_RETURN(!tmp, LOGMEM(type_mod->ctx), 0);
325 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);
335 LY_CHECK_ERR_RETURN(!tmp, LOGMEM(type_mod->ctx), 0);
336 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 }
342 }
343 } 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);
349 LY_CHECK_ERR_RETURN(!tmp, LOGMEM(type_mod->ctx), 0);
350 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);
359 LY_CHECK_ERR_RETURN(!tmp, LOGMEM(type_mod->ctx), 0);
360 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 }
373 }
374
375 return printed;
376 }
377
378 static int
tree_print_config(struct lyout * out,const struct lys_node * node,int spec_config)379 tree_print_config(struct lyout *out, const struct lys_node *node, int spec_config)
380 {
381 int ret;
382
383 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;
395 }
396
397 if (spec_config == 1) {
398 ret = ly_print(out, "-w ");
399 } else if (spec_config == 2) {
400 ret = ly_print(out, "ro ");
401 } else {
402 ret = ly_print(out, "%s ", (node->flags & LYS_CONFIG_W) ? "rw" : (node->flags & LYS_CONFIG_R) ? "ro" : "--");
403 }
404
405 if (node->nodetype == LYS_CHOICE) {
406 ret += ly_print(out, "(");
407 }
408 return ret;
409 }
410
411 static int
tree_print_features(struct lyout * out,struct lys_iffeature * iff1,uint8_t iff1_size,struct lys_iffeature * iff2,uint8_t iff2_size,tp_opts * opts,const char ** out_str)412 tree_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)
414 {
415 int i, printed;
416 struct lyout *o;
417
418 if (!iff1_size && !iff2_size) {
419 return 0;
420 }
421
422 if (out_str) {
423 o = malloc(sizeof *o);
424 LY_CHECK_ERR_RETURN(!o, LOGMEM(NULL), 0);
425 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
456 static int
tree_print_keys(struct lyout * out,struct lys_node_leaf ** keys,uint8_t keys_size,tp_opts * opts,const char ** out_str)457 tree_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);
468 LY_CHECK_ERR_RETURN(!o, LOGMEM(NULL), 0);
469 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 */
501 static void
tree_print_snode(struct lyout * out,int level,uint16_t max_name_len,const struct lys_node * node,int mask,const struct lys_node * aug_parent,int subtree,tp_opts * opts)502 tree_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)) {
513 return;
514 }
515
516 /* 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 }
521 }
522
523 /* special uses and grouping handling */
524 switch (node->nodetype & mask) {
525 case LYS_USES:
526 if (opts->options & LYS_OUTOPT_TREE_USES) {
527 break;
528 }
529 /* fallthrough */
530 case LYS_GROUPING:
531 goto print_children;
532 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;
538 default:
539 break;
540 }
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) {
555 case LYS_LEAF:
556 if (!(node->flags & LYS_MAND_TRUE) && !tree_leaf_is_mandatory(node)) {
557 node_len += ly_print(out, "?");
558 }
559 break;
560 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 }
570 break;
571 case LYS_LIST:
572 case LYS_LEAFLIST:
573 node_len += ly_print(out, "*");
574 break;
575 case LYS_CASE:
576 /* 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 /* set special config flag */
694 switch (node->nodetype & mask) {
695 case LYS_INPUT:
696 opts->spec_config = 1;
697 break;
698 case LYS_OUTPUT:
699 case LYS_NOTIF:
700 opts->spec_config = 2;
701 break;
702 case LYS_USES:
703 /* nothing more to print */
704 return;
705 default:
706 break;
707 }
708
709 if (subtree == 1) {
710 /* we are printing subtree parents, finish here */
711 return;
712 }
713
714 print_children:
715 /* set child mask and learn the longest child name (needed only if a child can have type) */
716 switch (node->nodetype & mask) {
717 case LYS_LEAF:
718 case LYS_LEAFLIST:
719 case LYS_ANYDATA:
720 case LYS_ANYXML:
721 child_mask = 0;
722 max_child_len = 0;
723 break;
724 case LYS_RPC:
725 case LYS_ACTION:
726 child_mask = LYS_INPUT | LYS_OUTPUT;
727 max_child_len = 0;
728 break;
729 case LYS_CHOICE:
730 child_mask = LYS_CASE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA;
731 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
732 break;
733 case LYS_CASE:
734 case LYS_NOTIF:
735 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES;
736 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
737 break;
738 case LYS_INPUT:
739 case LYS_OUTPUT:
740 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES;
741 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
742 break;
743 case LYS_USES:
744 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES | LYS_ACTION | LYS_NOTIF;
745 /* inherit the name length from the parent, it does not change */
746 max_child_len = max_name_len;
747 break;
748 case LYS_CONTAINER:
749 case LYS_LIST:
750 case LYS_GROUPING:
751 child_mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES | LYS_ACTION | LYS_NOTIF;
752 max_child_len = tree_get_max_name_len(node->child, NULL, child_mask, opts);
753 break;
754 default:
755 child_mask = 0;
756 max_child_len = 0;
757 LOGINT(node->module->ctx);
758 break;
759 }
760
761 /* print descendants (children) */
762 if (child_mask) {
763 LY_TREE_FOR(node->child, sub) {
764 /* submodule, foreign augments */
765 if (opts->module->type && (sub->parent != node) && (sub->module != opts->module)) {
766 continue;
767 }
768 tree_print_snode(out, level, max_child_len, sub, child_mask, NULL, 0, opts);
769 }
770 }
771
772 /* reset special config flag */
773 switch (node->nodetype & mask) {
774 case LYS_INPUT:
775 case LYS_OUTPUT:
776 case LYS_NOTIF:
777 opts->spec_config = 0;
778 break;
779 default:
780 break;
781 }
782 }
783
784 static void
tree_print_subtree(struct lyout * out,const struct lys_node * node,tp_opts * opts)785 tree_print_subtree(struct lyout *out, const struct lys_node *node, tp_opts *opts)
786 {
787 unsigned int depth, i, j;
788 int level = 0;
789 uint16_t max_child_len;
790 const struct lys_node *parent;
791
792 /* learn the depth of the node */
793 depth = 0;
794 parent = node;
795 while (lys_parent(parent)) {
796 if (lys_parent(parent)->nodetype != LYS_USES) {
797 ++depth;
798 }
799 parent = lys_parent(parent);
800 }
801
802 if (parent->nodetype == LYS_RPC) {
803 ly_print(out, "\n%*srpcs:\n", LY_TREE_MOD_DATA_INDENT, "");
804 opts->base_indent = LY_TREE_OP_DATA_INDENT;
805 } else if (parent->nodetype == LYS_NOTIF) {
806 ly_print(out, "\n%*snotifications:\n", LY_TREE_MOD_DATA_INDENT, "");
807 opts->base_indent = LY_TREE_OP_DATA_INDENT;
808 }
809
810 /* print all the parents */
811 if (depth) {
812 i = depth;
813 do {
814 parent = node;
815 for (j = 0; j < i; ++j) {
816 do {
817 parent = lys_parent(parent);
818 } while (parent->nodetype == LYS_USES);
819 }
820
821 tree_print_snode(out, level, 0, parent, LYS_CONTAINER | LYS_LIST | LYS_NOTIF | LYS_RPC | LYS_ACTION
822 | LYS_INPUT | LYS_OUTPUT, NULL, 1, opts);
823
824 ++level;
825 --i;
826 } while (i);
827 }
828
829 /* print the node and its descendants */
830 max_child_len = tree_get_max_name_len(node, NULL, LYS_LEAF|LYS_LEAFLIST|LYS_ANYDATA, opts);
831 tree_print_snode(out, level, max_child_len, node, LYS_ANY, NULL, 2, opts);
832 }
833
834 static int
tree_print_aug_target(struct lyout * out,int line_printed,uint8_t indent,const char * path,tp_opts * opts)835 tree_print_aug_target(struct lyout *out, int line_printed, uint8_t indent, const char *path, tp_opts *opts)
836 {
837 int printed, is_last, len;
838 const char *cur, *next;
839
840 printed = line_printed;
841 cur = path;
842 do {
843 next = strchr(cur + 1, '/');
844 if (!next) {
845 len = strlen(cur) + 1;
846 is_last = 1;
847 } else {
848 len = next - cur;
849 is_last = 0;
850 }
851
852 if (opts->line_length && cur != path && (printed + len > opts->line_length)) {
853 /* line_printed is treated as the base indent */
854 printed = ly_print(out, "\n%*s", line_printed + indent, "");
855 /* minus the newline */
856 --printed;
857 }
858 printed += ly_print(out, "%.*s%s", len, cur, is_last ? ":" : "");
859
860 cur = next;
861 } while (!is_last);
862
863 return printed;
864 }
865
866 int
tree_print_model(struct lyout * out,const struct lys_module * module,const char * target_schema_path,int ll,int options)867 tree_print_model(struct lyout *out, const struct lys_module *module, const char *target_schema_path,
868 int ll, int options)
869 {
870 struct lys_node *node = NULL, *data, *aug;
871 struct ly_set *set;
872 uint16_t max_child_len;
873 int have_rpcs = 0, have_notifs = 0, have_grps = 0, have_augs = 0, printed;
874 const char *str;
875 int i, mask;
876 tp_opts opts;
877
878 memset(&opts, 0, sizeof opts);
879 opts.module = module;
880 opts.line_length = ll;
881 opts.options = options;
882
883 /* we are printing only a subtree */
884 if (target_schema_path) {
885 set = lys_find_path(module, NULL, target_schema_path);
886 if (!set) {
887 return EXIT_FAILURE;
888 } else if (set->number != 1) {
889 LOGVAL(module->ctx, LYE_PATH_INNODE, LY_VLOG_NONE, NULL);
890 if (set->number == 0) {
891 LOGVAL(module->ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Schema path \"%s\" did not match any nodes.", target_schema_path);
892 } else {
893 LOGVAL(module->ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Schema path \"%s\" matched more nodes.", target_schema_path);
894 }
895 ly_set_free(set);
896 return EXIT_FAILURE;
897 }
898
899 node = set->set.s[0];
900 ly_set_free(set);
901 }
902
903 if (module->type) {
904 ly_print(out, "submodule: %s", module->name);
905 data = ((struct lys_submodule *)module)->belongsto->data;
906 if (options & LYS_OUTOPT_TREE_RFC) {
907 ly_print(out, "\n");
908 } else {
909 ly_print(out, " (belongs-to %s)\n", ((struct lys_submodule *)module)->belongsto->name);
910 }
911 } else {
912 ly_print(out, "module: %s\n", module->name);
913 data = module->data;
914 }
915
916 /* only subtree */
917 if (target_schema_path) {
918 opts.base_indent = LY_TREE_MOD_DATA_INDENT;
919 tree_print_subtree(out, node, &opts);
920 return EXIT_SUCCESS;
921 }
922
923 /* module */
924 opts.base_indent = LY_TREE_MOD_DATA_INDENT;
925 mask = LYS_CHOICE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES;
926 max_child_len = tree_get_max_name_len(data, NULL, mask, &opts);
927 LY_TREE_FOR(data, node) {
928 if (opts.module->type && (node->module != opts.module)) {
929 /* we're printing the submodule only */
930 continue;
931 }
932
933 switch (node->nodetype) {
934 case LYS_RPC:
935 if (!lys_is_disabled(node, 0)) {
936 have_rpcs++;
937 }
938 break;
939 case LYS_NOTIF:
940 if (!lys_is_disabled(node, 0)) {
941 have_notifs++;
942 }
943 break;
944 case LYS_GROUPING:
945 if ((options & LYS_OUTOPT_TREE_GROUPING) && !lys_is_disabled(node, 0)) {
946 have_grps++;
947 }
948 break;
949 default:
950 tree_print_snode(out, 0, max_child_len, node, mask, NULL, 0, &opts);
951 break;
952 }
953 }
954
955 /* all remaining nodes printed with operation indent */
956 opts.base_indent = LY_TREE_OP_DATA_INDENT;
957
958 /* augments */
959 for (i = 0; i < module->augment_size; i++) {
960 if ((module->type && (module->augment[i].target->module == module))
961 || (!module->type && (lys_node_module(module->augment[i].target) == module))
962 || lys_is_disabled((struct lys_node *)&module->augment[i], 0)) {
963 /* submodule, target is our submodule or module, target is in our module or any submodules */
964 continue;
965 }
966
967 if (!have_augs) {
968 ly_print(out, "\n");
969 have_augs = 1;
970 }
971
972 printed = ly_print(out, "%*saugment ", LY_TREE_MOD_DATA_INDENT, "");
973 if (options & LYS_OUTOPT_TREE_RFC) {
974 str = transform_json2schema(module, module->augment[i].target_name);
975 tree_print_aug_target(out, printed, LY_TREE_WRAP_INDENT, str, &opts);
976 lydict_remove(module->ctx, str);
977 } else {
978 tree_print_aug_target(out, printed, LY_TREE_WRAP_INDENT, module->augment[i].target_name, &opts);
979 }
980 ly_print(out, "\n");
981
982 aug = (struct lys_node *)&module->augment[i];
983 mask = LYS_CHOICE | LYS_CASE | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_USES
984 | LYS_ACTION | LYS_NOTIF;
985 max_child_len = tree_get_max_name_len(aug->child, aug, mask, &opts);
986 LY_TREE_FOR(aug->child, node) {
987 /* submodule, foreign augments */
988 if (node->parent != aug) {
989 continue;
990 }
991 tree_print_snode(out, 0, max_child_len, node, mask, aug, 0, &opts);
992 }
993 }
994
995 /* rpcs */
996 if (have_rpcs) {
997 ly_print(out, "\n%*srpcs:\n", LY_TREE_MOD_DATA_INDENT, "");
998
999 LY_TREE_FOR(data, node) {
1000 tree_print_snode(out, 0, 0, node, LYS_RPC, NULL, 0, &opts);
1001 }
1002 }
1003
1004 /* notifications */
1005 if (have_notifs) {
1006 ly_print(out, "\n%*snotifications:\n", LY_TREE_MOD_DATA_INDENT, "");
1007
1008 LY_TREE_FOR(data, node) {
1009 tree_print_snode(out, 0, 0, node, LYS_NOTIF, NULL, 0, &opts);
1010 }
1011 }
1012
1013 /* groupings */
1014 if ((options & LYS_OUTOPT_TREE_GROUPING) && have_grps) {
1015 ly_print(out, "\n");
1016 LY_TREE_FOR(data, node) {
1017 if (node->nodetype == LYS_GROUPING) {
1018 ly_print(out, "%*sgrouping %s:\n", LY_TREE_MOD_DATA_INDENT, "", node->name);
1019
1020 tree_print_snode(out, 0, 0, node, LYS_GROUPING, NULL, 0, &opts);
1021 }
1022 }
1023 }
1024
1025 ly_print_flush(out);
1026
1027 return EXIT_SUCCESS;
1028 }
1029