1 /**
2  * @file printer.c
3  * @author Radek Krejci <rkrejci@cesnet.cz>
4  * @brief Wrapper for all libyang printers.
5  *
6  * Copyright (c) 2015 - 2018 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 #define _GNU_SOURCE /* vasprintf(), vdprintf() */
16 #define _POSIX_C_SOURCE 200809L
17 
18 #include <sys/types.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 
26 #include "common.h"
27 #include "tree_schema.h"
28 #include "tree_data.h"
29 #include "printer.h"
30 
31 struct ext_substmt_info_s ext_substmt_info[] = {
32   {NULL, NULL, 0},                              /**< LYEXT_SUBSTMT_SELF */
33   {"argument", "name", SUBST_FLAG_ID},          /**< LYEXT_SUBSTMT_ARGUMENT */
34   {"base", "name", SUBST_FLAG_ID},              /**< LYEXT_SUBSTMT_BASE */
35   {"belongs-to", "module", SUBST_FLAG_ID},      /**< LYEXT_SUBSTMT_BELONGSTO */
36   {"contact", "text", SUBST_FLAG_YIN},          /**< LYEXT_SUBSTMT_CONTACT */
37   {"default", "value", 0},                      /**< LYEXT_SUBSTMT_DEFAULT */
38   {"description", "text", SUBST_FLAG_YIN},      /**< LYEXT_SUBSTMT_DESCRIPTION */
39   {"error-app-tag", "value", 0},                /**< LYEXT_SUBSTMT_ERRTAG */
40   {"error-message", "value", SUBST_FLAG_YIN},   /**< LYEXT_SUBSTMT_ERRMSG */
41   {"key", "value", 0},                          /**< LYEXT_SUBSTMT_KEY */
42   {"namespace", "uri", 0},                      /**< LYEXT_SUBSTMT_NAMESPACE */
43   {"organization", "text", SUBST_FLAG_YIN},     /**< LYEXT_SUBSTMT_ORGANIZATION */
44   {"path", "value", 0},                         /**< LYEXT_SUBSTMT_PATH */
45   {"prefix", "value", SUBST_FLAG_ID},           /**< LYEXT_SUBSTMT_PREFIX */
46   {"presence", "value", 0},                     /**< LYEXT_SUBSTMT_PRESENCE */
47   {"reference", "text", SUBST_FLAG_YIN},        /**< LYEXT_SUBSTMT_REFERENCE */
48   {"revision-date", "date", SUBST_FLAG_ID},     /**< LYEXT_SUBSTMT_REVISIONDATE */
49   {"units", "name", 0},                         /**< LYEXT_SUBSTMT_UNITS */
50   {"value", "value", SUBST_FLAG_ID},            /**< LYEXT_SUBSTMT_VALUE */
51   {"yang-version", "value", SUBST_FLAG_ID},     /**< LYEXT_SUBSTMT_VERSION */
52   {"modifier", "value", SUBST_FLAG_ID},         /**< LYEXT_SUBSTMT_MODIFIER */
53   {"require-instance", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REQINST */
54   {"yin-element", "value", SUBST_FLAG_ID},      /**< LYEXT_SUBSTMT_YINELEM */
55   {"config", "value", SUBST_FLAG_ID},           /**< LYEXT_SUBSTMT_CONFIG */
56   {"mandatory", "value", SUBST_FLAG_ID},        /**< LYEXT_SUBSTMT_MANDATORY */
57   {"ordered-by", "value", SUBST_FLAG_ID},       /**< LYEXT_SUBSTMT_ORDEREDBY */
58   {"status", "value", SUBST_FLAG_ID},           /**< LYEXT_SUBSTMT_STATUS */
59   {"fraction-digits", "value", SUBST_FLAG_ID},  /**< LYEXT_SUBSTMT_DIGITS */
60   {"max-elements", "value", SUBST_FLAG_ID},     /**< LYEXT_SUBSTMT_MAX */
61   {"min-elements", "value", SUBST_FLAG_ID},     /**< LYEXT_SUBSTMT_MIN */
62   {"position", "value", SUBST_FLAG_ID},         /**< LYEXT_SUBSTMT_POSITION */
63   {"unique", "tag", 0},                         /**< LYEXT_SUBSTMT_UNIQUE */
64 };
65 
66 /* 0 - same, 1 - different */
67 int
nscmp(const struct lyd_node * node1,const struct lyd_node * node2)68 nscmp(const struct lyd_node *node1, const struct lyd_node *node2)
69 {
70     /* we have to cover submodules belonging to the same module */
71     if (lys_node_module(node1->schema) == lys_node_module(node2->schema)) {
72         /* belongs to the same module */
73         return 0;
74     } else {
75         /* different modules */
76         return 1;
77     }
78 }
79 
80 int
ly_print(struct lyout * out,const char * format,...)81 ly_print(struct lyout *out, const char *format, ...)
82 {
83     int count = 0;
84     char *msg = NULL, *aux;
85     va_list ap;
86 
87     va_start(ap, format);
88 
89     switch (out->type) {
90     case LYOUT_FD:
91         count = vdprintf(out->method.fd, format, ap);
92         break;
93     case LYOUT_STREAM:
94         count = vfprintf(out->method.f, format, ap);
95         break;
96     case LYOUT_MEMORY:
97         count = vasprintf(&msg, format, ap);
98         if (out->method.mem.len + count + 1 > out->method.mem.size) {
99             aux = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
100             if (!aux) {
101                 out->method.mem.buf = NULL;
102                 out->method.mem.len = 0;
103                 out->method.mem.size = 0;
104                 LOGMEM(NULL);
105                 free(msg);
106                 va_end(ap);
107                 return -1;
108             }
109             out->method.mem.buf = aux;
110             out->method.mem.size = out->method.mem.len + count + 1;
111         }
112         memcpy(&out->method.mem.buf[out->method.mem.len], msg, count);
113         out->method.mem.len += count;
114         out->method.mem.buf[out->method.mem.len] = '\0';
115         free(msg);
116         break;
117     case LYOUT_CALLBACK:
118         count = vasprintf(&msg, format, ap);
119         count = out->method.clb.f(out->method.clb.arg, msg, count);
120         if (count >= 0) {
121             /*
122              * Depending on what the callback function does, errno might
123              * contain non-zero values that are not real "errors" (EAGAIN or
124              * EINTR). Reset errno if the callback returns a zero or positive
125              * value.
126              */
127             errno = 0;
128         }
129         free(msg);
130         break;
131     }
132 
133     va_end(ap);
134     return count;
135 }
136 
137 void
ly_print_flush(struct lyout * out)138 ly_print_flush(struct lyout *out)
139 {
140     switch (out->type) {
141     case LYOUT_STREAM:
142         fflush(out->method.f);
143         break;
144     case LYOUT_FD:
145     case LYOUT_MEMORY:
146     case LYOUT_CALLBACK:
147         /* nothing to do */
148         break;
149     }
150 }
151 
152 int
ly_write(struct lyout * out,const char * buf,size_t count)153 ly_write(struct lyout *out, const char *buf, size_t count)
154 {
155     if (out->hole_count) {
156         /* we are buffering data after a hole */
157         if (out->buf_len + count > out->buf_size) {
158             out->buffered = ly_realloc(out->buffered, out->buf_len + count);
159             if (!out->buffered) {
160                 out->buf_len = 0;
161                 out->buf_size = 0;
162                 LOGMEM(NULL);
163                 return -1;
164             }
165             out->buf_size = out->buf_len + count;
166         }
167 
168         memcpy(&out->buffered[out->buf_len], buf, count);
169         out->buf_len += count;
170         return count;
171     }
172 
173     switch (out->type) {
174     case LYOUT_MEMORY:
175         if (out->method.mem.len + count + 1 > out->method.mem.size) {
176             out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
177             if (!out->method.mem.buf) {
178                 out->method.mem.len = 0;
179                 out->method.mem.size = 0;
180                 LOGMEM(NULL);
181                 return -1;
182             }
183             out->method.mem.size = out->method.mem.len + count + 1;
184         }
185         memcpy(&out->method.mem.buf[out->method.mem.len], buf, count);
186         out->method.mem.len += count;
187         out->method.mem.buf[out->method.mem.len] = '\0';
188         return count;
189     case LYOUT_FD:
190         return write(out->method.fd, buf, count);
191     case LYOUT_STREAM:
192         return fwrite(buf, sizeof *buf, count, out->method.f);
193     case LYOUT_CALLBACK:
194         return out->method.clb.f(out->method.clb.arg, buf, count);
195     }
196 
197     return 0;
198 }
199 
200 int
ly_write_skip(struct lyout * out,size_t count,size_t * position)201 ly_write_skip(struct lyout *out, size_t count, size_t *position)
202 {
203     switch (out->type) {
204     case LYOUT_MEMORY:
205         if (out->method.mem.len + count > out->method.mem.size) {
206             out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count);
207             if (!out->method.mem.buf) {
208                 out->method.mem.len = 0;
209                 out->method.mem.size = 0;
210                 LOGMEM(NULL);
211                 return -1;
212             }
213             out->method.mem.size = out->method.mem.len + count;
214         }
215 
216         /* save the current position */
217         *position = out->method.mem.len;
218 
219         /* skip the memory */
220         out->method.mem.len += count;
221         break;
222     case LYOUT_FD:
223     case LYOUT_STREAM:
224     case LYOUT_CALLBACK:
225         /* buffer the hole */
226         if (out->buf_len + count > out->buf_size) {
227             out->buffered = ly_realloc(out->buffered, out->buf_len + count);
228             if (!out->buffered) {
229                 out->buf_len = 0;
230                 out->buf_size = 0;
231                 LOGMEM(NULL);
232                 return -1;
233             }
234             out->buf_size = out->buf_len + count;
235         }
236 
237         /* save the current position */
238         *position = out->buf_len;
239 
240         /* skip the memory */
241         out->buf_len += count;
242 
243         /* increase hole counter */
244         ++out->hole_count;
245     }
246 
247     return count;
248 }
249 
250 int
ly_write_skipped(struct lyout * out,size_t position,const char * buf,size_t count)251 ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t count)
252 {
253     switch (out->type) {
254     case LYOUT_MEMORY:
255         /* write */
256         memcpy(&out->method.mem.buf[position], buf, count);
257         break;
258     case LYOUT_FD:
259     case LYOUT_STREAM:
260     case LYOUT_CALLBACK:
261         if (out->buf_len < position + count) {
262             LOGINT(NULL);
263             return -1;
264         }
265 
266         /* write into the hole */
267         memcpy(&out->buffered[position], buf, count);
268 
269         /* decrease hole counter */
270         --out->hole_count;
271 
272         if (!out->hole_count) {
273             /* all holes filled, we can write the buffer */
274             count = ly_write(out, out->buffered, out->buf_len);
275             out->buf_len = 0;
276         }
277         break;
278     }
279 
280     return count;
281 }
282 
283 static int
write_iff(struct lyout * out,const struct lys_module * module,struct lys_iffeature * expr,int prefix_kind,int * index_e,int * index_f)284 write_iff(struct lyout *out, const struct lys_module *module, struct lys_iffeature *expr, int prefix_kind,
285           int *index_e, int *index_f)
286 {
287     int count = 0, brackets_flag = *index_e;
288     uint8_t op;
289     struct lys_module *mod;
290 
291     op = iff_getop(expr->expr, *index_e);
292     (*index_e)++;
293 
294     switch (op) {
295     case LYS_IFF_F:
296         if (lys_main_module(expr->features[*index_f]->module) != lys_main_module(module)) {
297             if (prefix_kind == 0) {
298                 count += ly_print(out, "%s:", transform_module_name2import_prefix(module,
299                                   lys_main_module(expr->features[*index_f]->module)->name));
300             } else if (prefix_kind == 1) {
301                 count += ly_print(out, "%s:", lys_main_module(expr->features[*index_f]->module)->name);
302             } else if (prefix_kind == 2) {
303                 count += ly_print(out, "%s:", lys_main_module(expr->features[*index_f]->module)->prefix);
304             } else if (prefix_kind == 3) {
305                 mod =  lys_main_module(expr->features[*index_f]->module);
306                 count += ly_print(out, "%s%s%s:", mod->name, mod->rev_size ? "@" : "", mod->rev_size ? mod->rev[0].date : "");
307             }
308         }
309         count += ly_print(out, expr->features[*index_f]->name);
310         (*index_f)++;
311         break;
312     case LYS_IFF_NOT:
313         count += ly_print(out, "not ");
314         count += write_iff(out, module, expr, prefix_kind, index_e, index_f);
315         break;
316     case LYS_IFF_AND:
317         if (brackets_flag) {
318             /* AND need brackets only if previous op was not */
319             if (*index_e < 2 || iff_getop(expr->expr, *index_e - 2) != LYS_IFF_NOT) {
320                 brackets_flag = 0;
321             }
322         }
323         /* falls through */
324     case LYS_IFF_OR:
325         if (brackets_flag) {
326             count += ly_print(out, "(");
327         }
328         count += write_iff(out, module, expr, prefix_kind, index_e, index_f);
329         count += ly_print(out, " %s ", op == LYS_IFF_OR ? "or" : "and");
330         count += write_iff(out, module, expr, prefix_kind, index_e, index_f);
331         if (brackets_flag) {
332             count += ly_print(out, ")");
333         }
334     }
335 
336     return count;
337 }
338 
339 int
ly_print_iffeature(struct lyout * out,const struct lys_module * module,struct lys_iffeature * expr,int prefix_kind)340 ly_print_iffeature(struct lyout *out, const struct lys_module *module, struct lys_iffeature *expr, int prefix_kind)
341 {
342     int index_e = 0, index_f = 0;
343 
344     if (expr->expr) {
345         return write_iff(out, module, expr, prefix_kind, &index_e, &index_f);
346     }
347 
348     return 0;
349 }
350 
351 static int
lys_print_(struct lyout * out,const struct lys_module * module,LYS_OUTFORMAT format,const char * target_node,int line_length,int options)352 lys_print_(struct lyout *out, const struct lys_module *module, LYS_OUTFORMAT format, const char *target_node,
353            int line_length, int options)
354 {
355     int ret;
356 
357     switch (format) {
358     case LYS_OUT_YIN:
359         lys_disable_deviations((struct lys_module *)module);
360         ret = yin_print_model(out, module);
361         lys_enable_deviations((struct lys_module *)module);
362         break;
363     case LYS_OUT_YANG:
364         lys_disable_deviations((struct lys_module *)module);
365         ret = yang_print_model(out, module);
366         lys_enable_deviations((struct lys_module *)module);
367         break;
368     case LYS_OUT_TREE:
369         ret = tree_print_model(out, module, target_node, line_length, options);
370         break;
371     case LYS_OUT_INFO:
372         ret = info_print_model(out, module, target_node);
373         break;
374     case LYS_OUT_JSON:
375         ret = jsons_print_model(out, module, target_node);
376         break;
377     default:
378         LOGERR(module->ctx, LY_EINVAL, "Unknown output format.");
379         ret = EXIT_FAILURE;
380         break;
381     }
382 
383     return ret;
384 }
385 
386 API int
lys_print_file(FILE * f,const struct lys_module * module,LYS_OUTFORMAT format,const char * target_node,int line_length,int options)387 lys_print_file(FILE *f, const struct lys_module *module, LYS_OUTFORMAT format, const char *target_node,
388                int line_length, int options)
389 {
390     struct lyout out;
391 
392     if (!f || !module) {
393         LOGARG;
394         return EXIT_FAILURE;
395     }
396 
397     memset(&out, 0, sizeof out);
398 
399     out.type = LYOUT_STREAM;
400     out.method.f = f;
401 
402     return lys_print_(&out, module, format, target_node, line_length, options);
403 }
404 
405 API int
lys_print_path(const char * path,const struct lys_module * module,LYS_OUTFORMAT format,const char * target_node,int line_length,int options)406 lys_print_path(const char *path, const struct lys_module *module, LYS_OUTFORMAT format, const char *target_node,
407                int line_length, int options)
408 {
409     FILE *f;
410     int ret;
411 
412     if (!path || !module) {
413         LOGARG;
414         return EXIT_FAILURE;
415     }
416 
417     f = fopen(path, "w");
418     if (!f) {
419         LOGERR(module->ctx, LY_ESYS, "Failed to open file \"%s\" (%s).", path, strerror(errno));
420         return EXIT_FAILURE;
421     }
422 
423     ret = lys_print_file(f, module, format, target_node, line_length, options);
424     fclose(f);
425     return ret;
426 }
427 
428 API int
lys_print_fd(int fd,const struct lys_module * module,LYS_OUTFORMAT format,const char * target_node,int line_length,int options)429 lys_print_fd(int fd, const struct lys_module *module, LYS_OUTFORMAT format, const char *target_node,
430              int line_length, int options)
431 {
432     struct lyout out;
433 
434     if (fd < 0 || !module) {
435         LOGARG;
436         return EXIT_FAILURE;
437     }
438 
439     memset(&out, 0, sizeof out);
440 
441     out.type = LYOUT_FD;
442     out.method.fd = fd;
443 
444     return lys_print_(&out, module, format, target_node, line_length, options);
445 }
446 
447 API int
lys_print_mem(char ** strp,const struct lys_module * module,LYS_OUTFORMAT format,const char * target_node,int line_length,int options)448 lys_print_mem(char **strp, const struct lys_module *module, LYS_OUTFORMAT format, const char *target_node,
449               int line_length, int options)
450 {
451     struct lyout out;
452     int r;
453 
454     if (!strp || !module) {
455         LOGARG;
456         return EXIT_FAILURE;
457     }
458 
459     memset(&out, 0, sizeof out);
460 
461     out.type = LYOUT_MEMORY;
462 
463     r = lys_print_(&out, module, format, target_node, line_length, options);
464 
465     *strp = out.method.mem.buf;
466     return r;
467 }
468 
469 API int
lys_print_clb(ssize_t (* writeclb)(void * arg,const void * buf,size_t count),void * arg,const struct lys_module * module,LYS_OUTFORMAT format,const char * target_node,int line_length,int options)470 lys_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lys_module *module,
471               LYS_OUTFORMAT format, const char *target_node, int line_length, int options)
472 {
473     struct lyout out;
474 
475     if (!writeclb || !module) {
476         LOGARG;
477         return EXIT_FAILURE;
478     }
479 
480     memset(&out, 0, sizeof out);
481 
482     out.type = LYOUT_CALLBACK;
483     out.method.clb.f = writeclb;
484     out.method.clb.arg = arg;
485 
486     return lys_print_(&out, module, format, target_node, line_length, options);
487 }
488 
489 int
lys_print_target(struct lyout * out,const struct lys_module * module,const char * target_schema_path,void (* clb_print_typedef)(struct lyout *,const struct lys_tpdf *,int *),void (* clb_print_identity)(struct lyout *,const struct lys_ident *,int *),void (* clb_print_feature)(struct lyout *,const struct lys_feature *,int *),void (* clb_print_type)(struct lyout *,const struct lys_type *,int *),void (* clb_print_grouping)(struct lyout *,const struct lys_node *,int *),void (* clb_print_container)(struct lyout *,const struct lys_node *,int *),void (* clb_print_choice)(struct lyout *,const struct lys_node *,int *),void (* clb_print_leaf)(struct lyout *,const struct lys_node *,int *),void (* clb_print_leaflist)(struct lyout *,const struct lys_node *,int *),void (* clb_print_list)(struct lyout *,const struct lys_node *,int *),void (* clb_print_anydata)(struct lyout *,const struct lys_node *,int *),void (* clb_print_case)(struct lyout *,const struct lys_node *,int *),void (* clb_print_notif)(struct lyout *,const struct lys_node *,int *),void (* clb_print_rpc)(struct lyout *,const struct lys_node *,int *),void (* clb_print_action)(struct lyout *,const struct lys_node *,int *),void (* clb_print_input)(struct lyout *,const struct lys_node *,int *),void (* clb_print_output)(struct lyout *,const struct lys_node *,int *))490 lys_print_target(struct lyout *out, const struct lys_module *module, const char *target_schema_path,
491                  void (*clb_print_typedef)(struct lyout*, const struct lys_tpdf*, int*),
492                  void (*clb_print_identity)(struct lyout*, const struct lys_ident*, int*),
493                  void (*clb_print_feature)(struct lyout*, const struct lys_feature*, int*),
494                  void (*clb_print_type)(struct lyout*, const struct lys_type*, int*),
495                  void (*clb_print_grouping)(struct lyout*, const struct lys_node*, int*),
496                  void (*clb_print_container)(struct lyout*, const struct lys_node*, int*),
497                  void (*clb_print_choice)(struct lyout*, const struct lys_node*, int*),
498                  void (*clb_print_leaf)(struct lyout*, const struct lys_node*, int*),
499                  void (*clb_print_leaflist)(struct lyout*, const struct lys_node*, int*),
500                  void (*clb_print_list)(struct lyout*, const struct lys_node*, int*),
501                  void (*clb_print_anydata)(struct lyout*, const struct lys_node*, int*),
502                  void (*clb_print_case)(struct lyout*, const struct lys_node*, int*),
503                  void (*clb_print_notif)(struct lyout*, const struct lys_node*, int*),
504                  void (*clb_print_rpc)(struct lyout*, const struct lys_node*, int*),
505                  void (*clb_print_action)(struct lyout*, const struct lys_node*, int*),
506                  void (*clb_print_input)(struct lyout*, const struct lys_node*, int*),
507                  void (*clb_print_output)(struct lyout*, const struct lys_node*, int*))
508 {
509     int rc, i, f = 1;
510     char *spec_target = NULL;
511     struct lys_node *target = NULL;
512     struct lys_tpdf *tpdf = NULL;
513     uint8_t tpdf_size = 0;
514 
515     if ((target_schema_path[0] == '/') || !strncmp(target_schema_path, "type/", 5)) {
516         rc = resolve_absolute_schema_nodeid((target_schema_path[0] == '/' ? target_schema_path : target_schema_path + 4), module,
517                                             LYS_ANY & ~(LYS_USES | LYS_AUGMENT | LYS_GROUPING), (const struct lys_node **)&target);
518         if (rc || !target) {
519             LOGERR(module->ctx, LY_EINVAL, "Target %s could not be resolved.",
520                    (target_schema_path[0] == '/' ? target_schema_path : target_schema_path + 4));
521             return EXIT_FAILURE;
522         }
523     } else if (!strncmp(target_schema_path, "grouping/", 9)) {
524         /* cut the data part off */
525         if ((spec_target = strchr(target_schema_path + 9, '/'))) {
526             /* HACK only temporary */
527             spec_target[0] = '\0';
528             ++spec_target;
529         }
530         rc = resolve_absolute_schema_nodeid(target_schema_path + 8, module, LYS_GROUPING, (const struct lys_node **)&target);
531         if (rc || !target) {
532             ly_print(out, "Grouping %s not found.\n", target_schema_path + 8);
533             return EXIT_FAILURE;
534         }
535     } else if (!strncmp(target_schema_path, "typedef/", 8)) {
536         if ((spec_target = strrchr(target_schema_path + 8, '/'))) {
537             /* schema node typedef */
538             /* HACK only temporary */
539             spec_target[0] = '\0';
540             ++spec_target;
541 
542             rc = resolve_absolute_schema_nodeid(target_schema_path + 7, module,
543                                                 LYS_CONTAINER | LYS_LIST | LYS_NOTIF | LYS_RPC | LYS_ACTION,
544                                                 (const struct lys_node **)&target);
545             if (rc || !target) {
546                 /* perhaps it's in a grouping */
547                 rc = resolve_absolute_schema_nodeid(target_schema_path + 7, module, LYS_GROUPING,
548                                                     (const struct lys_node **)&target);
549             }
550             if (!rc && target) {
551                 switch (target->nodetype) {
552                 case LYS_CONTAINER:
553                     tpdf = ((struct lys_node_container *)target)->tpdf;
554                     tpdf_size = ((struct lys_node_container *)target)->tpdf_size;
555                     break;
556                 case LYS_LIST:
557                     tpdf = ((struct lys_node_list *)target)->tpdf;
558                     tpdf_size = ((struct lys_node_list *)target)->tpdf_size;
559                     break;
560                 case LYS_NOTIF:
561                     tpdf = ((struct lys_node_notif *)target)->tpdf;
562                     tpdf_size = ((struct lys_node_notif *)target)->tpdf_size;
563                     break;
564                 case LYS_RPC:
565                 case LYS_ACTION:
566                     tpdf = ((struct lys_node_rpc_action *)target)->tpdf;
567                     tpdf_size = ((struct lys_node_rpc_action *)target)->tpdf_size;
568                     break;
569                 case LYS_GROUPING:
570                     tpdf = ((struct lys_node_grp *)target)->tpdf;
571                     tpdf_size = ((struct lys_node_grp *)target)->tpdf_size;
572                     break;
573                 default:
574                     LOGINT(module->ctx);
575                     return EXIT_FAILURE;
576                 }
577             }
578         } else {
579             /* module typedef */
580             spec_target = (char *)target_schema_path + 8;
581             tpdf = module->tpdf;
582             tpdf_size = module->tpdf_size;
583         }
584 
585         for (i = 0; i < tpdf_size; ++i) {
586             if (!strcmp(tpdf[i].name, spec_target)) {
587                 clb_print_typedef(out, &tpdf[i], &f);
588                 break;
589             }
590         }
591         /* HACK return previous hack */
592         --spec_target;
593         spec_target[0] = '/';
594 
595         if (i == tpdf_size) {
596             ly_print(out, "Typedef %s not found.\n", target_schema_path);
597             return EXIT_FAILURE;
598         }
599         return EXIT_SUCCESS;
600 
601     } else if (!strncmp(target_schema_path, "identity/", 9)) {
602         target_schema_path += 9;
603         for (i = 0; i < (signed)module->ident_size; ++i) {
604             if (!strcmp(module->ident[i].name, target_schema_path)) {
605                 break;
606             }
607         }
608         if (i == (signed)module->ident_size) {
609             ly_print(out, "Identity %s not found.\n", target_schema_path);
610             return EXIT_FAILURE;
611         }
612 
613         clb_print_identity(out, &module->ident[i], &f);
614         return EXIT_SUCCESS;
615 
616     } else if (!strncmp(target_schema_path, "feature/", 8)) {
617         target_schema_path += 8;
618         for (i = 0; i < module->features_size; ++i) {
619             if (!strcmp(module->features[i].name, target_schema_path)) {
620                 break;
621             }
622         }
623         if (i == module->features_size) {
624             ly_print(out, "Feature %s not found.\n", target_schema_path);
625             return EXIT_FAILURE;
626         }
627 
628         clb_print_feature(out, &module->features[i], &f);
629         return EXIT_SUCCESS;
630     } else {
631         ly_print(out, "Target could not be resolved.\n");
632         return EXIT_FAILURE;
633     }
634 
635     if (!strncmp(target_schema_path, "type/", 5)) {
636         if (!(target->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
637             LOGERR(module->ctx, LY_EINVAL, "Target is not a leaf or a leaf-list.");
638             return EXIT_FAILURE;
639         }
640         clb_print_type(out, &((struct lys_node_leaf *)target)->type, &f);
641         return EXIT_SUCCESS;
642     } else if (!strncmp(target_schema_path, "grouping/", 9) && !spec_target) {
643         clb_print_grouping(out, target, &f);
644         return EXIT_SUCCESS;
645     }
646 
647     /* find the node in the grouping */
648     if (spec_target) {
649         rc = resolve_descendant_schema_nodeid(spec_target, target->child, LYS_NO_RPC_NOTIF_NODE,
650                                               0, (const struct lys_node **)&target);
651         if (rc || !target) {
652             ly_print(out, "Grouping %s child \"%s\" not found.\n", target_schema_path + 9, spec_target);
653             return EXIT_FAILURE;
654         }
655         /* HACK return previous hack */
656         --spec_target;
657         spec_target[0] = '/';
658     }
659     switch (target->nodetype) {
660     case LYS_CONTAINER:
661         clb_print_container(out, target, &f);
662         break;
663     case LYS_CHOICE:
664         clb_print_choice(out, target, &f);
665         break;
666     case LYS_LEAF:
667         clb_print_leaf(out, target, &f);
668         break;
669     case LYS_LEAFLIST:
670         clb_print_leaflist(out, target, &f);
671         break;
672     case LYS_LIST:
673         clb_print_list(out, target, &f);
674         break;
675     case LYS_ANYXML:
676     case LYS_ANYDATA:
677         clb_print_anydata(out, target, &f);
678         break;
679     case LYS_CASE:
680         clb_print_case(out, target, &f);
681         break;
682     case LYS_NOTIF:
683         clb_print_notif(out, target, &f);
684         break;
685     case LYS_RPC:
686         clb_print_rpc(out, target, &f);
687         break;
688     case LYS_ACTION:
689         clb_print_action(out, target, &f);
690         break;
691     case LYS_INPUT:
692         clb_print_input(out, target, &f);
693         break;
694     case LYS_OUTPUT:
695         clb_print_output(out, target, &f);
696         break;
697     default:
698         ly_print(out, "Nodetype %s not supported.\n", strnodetype(target->nodetype));
699         break;
700     }
701 
702     return EXIT_SUCCESS;
703 }
704 
705 static int
lyd_print_(struct lyout * out,const struct lyd_node * root,LYD_FORMAT format,int options)706 lyd_print_(struct lyout *out, const struct lyd_node *root, LYD_FORMAT format, int options)
707 {
708     switch (format) {
709     case LYD_XML:
710         return xml_print_data(out, root, options);
711     case LYD_JSON:
712         return json_print_data(out, root, options);
713     case LYD_LYB:
714         return lyb_print_data(out, root, options);
715     default:
716         LOGERR(root->schema->module->ctx, LY_EINVAL, "Unknown output format.");
717         return EXIT_FAILURE;
718     }
719 }
720 
721 API int
lyd_print_file(FILE * f,const struct lyd_node * root,LYD_FORMAT format,int options)722 lyd_print_file(FILE *f, const struct lyd_node *root, LYD_FORMAT format, int options)
723 {
724     int r;
725     struct lyout out;
726 
727     if (!f) {
728         LOGARG;
729         return EXIT_FAILURE;
730     }
731 
732     memset(&out, 0, sizeof out);
733 
734     out.type = LYOUT_STREAM;
735     out.method.f = f;
736 
737     r = lyd_print_(&out, root, format, options);
738 
739     free(out.buffered);
740     return r;
741 }
742 
743 API int
lyd_print_path(const char * path,const struct lyd_node * root,LYD_FORMAT format,int options)744 lyd_print_path(const char *path, const struct lyd_node *root, LYD_FORMAT format, int options)
745 {
746     FILE *f;
747     int ret;
748 
749     if (!path) {
750         LOGARG;
751         return EXIT_FAILURE;
752     }
753 
754     f = fopen(path, "w");
755     if (!f) {
756         LOGERR(root->schema->module->ctx, LY_EINVAL, "Cannot open file \"%s\" for writing.", path);
757         return EXIT_FAILURE;
758     }
759 
760     ret = lyd_print_file(f, root, format, options);
761 
762     fclose(f);
763     return ret;
764 }
765 
766 API int
lyd_print_fd(int fd,const struct lyd_node * root,LYD_FORMAT format,int options)767 lyd_print_fd(int fd, const struct lyd_node *root, LYD_FORMAT format, int options)
768 {
769     int r;
770     struct lyout out;
771 
772     if (fd < 0) {
773         LOGARG;
774         return EXIT_FAILURE;
775     }
776 
777     memset(&out, 0, sizeof out);
778 
779     out.type = LYOUT_FD;
780     out.method.fd = fd;
781 
782     r = lyd_print_(&out, root, format, options);
783 
784     free(out.buffered);
785     return r;
786 }
787 
788 API int
lyd_print_mem(char ** strp,const struct lyd_node * root,LYD_FORMAT format,int options)789 lyd_print_mem(char **strp, const struct lyd_node *root, LYD_FORMAT format, int options)
790 {
791     struct lyout out;
792     int r;
793 
794     if (!strp) {
795         LOGARG;
796         return EXIT_FAILURE;
797     }
798 
799     memset(&out, 0, sizeof out);
800 
801     out.type = LYOUT_MEMORY;
802 
803     r = lyd_print_(&out, root, format, options);
804 
805     *strp = out.method.mem.buf;
806     free(out.buffered);
807     return r;
808 }
809 
810 API int
lyd_print_clb(ssize_t (* writeclb)(void * arg,const void * buf,size_t count),void * arg,const struct lyd_node * root,LYD_FORMAT format,int options)811 lyd_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lyd_node *root,
812               LYD_FORMAT format, int options)
813 {
814     int r;
815     struct lyout out;
816 
817     if (!writeclb) {
818         LOGARG;
819         return EXIT_FAILURE;
820     }
821 
822     memset(&out, 0, sizeof out);
823 
824     out.type = LYOUT_CALLBACK;
825     out.method.clb.f = writeclb;
826     out.method.clb.arg = arg;
827 
828     r = lyd_print_(&out, root, format, options);
829 
830     free(out.buffered);
831     return r;
832 }
833 
834 static int
lyd_wd_toprint(const struct lyd_node * node,int options)835 lyd_wd_toprint(const struct lyd_node *node, int options)
836 {
837     const struct lyd_node *subroot, *next, *elem;
838     int flag = 0;
839 
840     if (options & LYP_WD_TRIM) {
841         /* do not print default nodes */
842         if (node->dflt) {
843             /* implicit default node */
844             return 0;
845         } else if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
846             if (lyd_wd_default((struct lyd_node_leaf_list *)node)) {
847                 /* explicit default node */
848                 return 0;
849             }
850         } else if ((node->schema->nodetype & (LYS_CONTAINER)) && !((struct lys_node_container *)node->schema)->presence) {
851             /* get know if non-presence container contains non-default node */
852             for (subroot = node->child; subroot && !flag; subroot = subroot->next) {
853                 LY_TREE_DFS_BEGIN(subroot, next, elem) {
854                     if (elem->dflt) {
855                         /* skip subtree */
856                         goto trim_dfs_nextsibling;
857                     }
858                     switch (elem->schema->nodetype) {
859                     case LYS_LEAF:
860                     case LYS_LEAFLIST:
861                         if (!lyd_wd_default((struct lyd_node_leaf_list *)elem)) {
862                             /* non-default node */
863                             flag = 1;
864                         }
865                         break;
866                     case LYS_ANYDATA:
867                     case LYS_ANYXML:
868                     case LYS_NOTIF:
869                     case LYS_ACTION:
870                     case LYS_LIST:
871                         /* non-default nodes */
872                         flag = 1;
873                         break;
874                     case LYS_CONTAINER:
875                         if (((struct lys_node_container *)elem->schema)->presence) {
876                             /* non-default node */
877                             flag = 1;
878                         }
879                         break;
880                     default:
881                         break;
882                     }
883                     if (flag) {
884                         break;
885                     }
886 
887                     /* modified LY_TREE_DFS_END */
888                     /* select element for the next run - children first */
889                     /* child exception for leafs, leaflists and anyxml without children */
890                     if (elem->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
891                         next = NULL;
892                     } else {
893                         next = elem->child;
894                     }
895                     if (!next) {
896 trim_dfs_nextsibling:
897                         /* no children */
898                         if (elem == subroot) {
899                             /* we are done, (START) has no children */
900                             break;
901                         }
902                         /* try siblings */
903                         next = elem->next;
904                     }
905                     while (!next) {
906                         /* parent is already processed, go to its sibling */
907                         elem = elem->parent;
908                         /* no siblings, go back through parents */
909                         if (elem->parent == subroot->parent) {
910                             /* we are done, no next element to process */
911                             break;
912                         }
913                         next = elem->next;
914                     }
915                 }
916             }
917             if (!flag) {
918                 /* only default nodes in subtree, do not print the container */
919                 return 0;
920             }
921         }
922     } else if (node->dflt && !(options & LYP_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
923         /* LYP_WD_EXPLICIT
924          * - print only if it contains status data in its subtree */
925         LY_TREE_DFS_BEGIN(node, next, elem) {
926             if ((elem->schema->nodetype != LYS_CONTAINER) || ((struct lys_node_container *)elem->schema)->presence) {
927                 if (elem->schema->flags & LYS_CONFIG_R) {
928                     flag = 1;
929                     break;
930                 }
931             }
932             LY_TREE_DFS_END(node, next, elem)
933         }
934         if (!flag) {
935             return 0;
936         }
937     } else if (node->dflt && node->schema->nodetype == LYS_CONTAINER && !(options & LYP_KEEPEMPTYCONT)) {
938         /* avoid empty default containers */
939         LY_TREE_DFS_BEGIN(node, next, elem) {
940             if (elem->schema->nodetype != LYS_CONTAINER) {
941                 flag = 1;
942                 break;
943             }
944             LY_TREE_DFS_END(node, next, elem)
945         }
946         if (!flag) {
947             return 0;
948         }
949     }
950 
951     return 1;
952 }
953 
954 API int
lyd_node_should_print(const struct lyd_node * node,int options)955 lyd_node_should_print(const struct lyd_node *node, int options)
956 {
957     struct lys_node *scase, *sparent;
958     struct lyd_node *first;
959 
960     if (!lyd_wd_toprint(node, options)) {
961         /* wd says do not print, but make exception for direct descendants of case nodes without other printable nodes */
962         for (sparent = lys_parent(node->schema); sparent && (sparent->nodetype == LYS_USES); sparent = lys_parent(sparent));
963         if (!sparent || (sparent->nodetype != LYS_CASE)) {
964             /* parent not a case */
965             return 0;
966         }
967         scase = sparent;
968 
969         for (sparent = lys_parent(scase); sparent && (sparent->nodetype == LYS_USES); sparent = lys_parent(sparent));
970         if (!sparent || (sparent->nodetype != LYS_CHOICE)) {
971             /* weird */
972             LOGINT(lyd_node_module(node)->ctx);
973             return 0;
974         }
975         if (((struct lys_node_choice *)sparent)->dflt == scase) {
976             /* this is a default case, respect the previous original toprint flag */
977             return 0;
978         }
979 
980         /* try to find a sibling that will be printed */
981         for (first = node->prev; first->prev->next; first = first->prev);
982         LY_TREE_FOR(first, first) {
983             if (first == node) {
984                 /* skip this node */
985                 continue;
986             }
987 
988             /* find schema parent, whether it is the same case */
989             for (sparent = lys_parent(first->schema); sparent && (sparent->nodetype == LYS_USES); sparent = lys_parent(sparent));
990             if ((sparent == scase) && lyd_wd_toprint(first, options)) {
991                 /* this other node will be printed, we do not have to print the current one */
992                 return 0;
993             }
994         }
995 
996         /* there is no case child that will be printed, print this node */
997         return 1;
998     }
999 
1000     return 1;
1001 }
1002