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