blob: 3eef1358aa78cb94928b0a4d17bdb0f7c498efd8 [file] [log] [blame]
Radek Krejcied5acc52019-04-25 15:57:04 +02001/**
2 * @file main_ni.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief libyang's yanglint tool - noninteractive code
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
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
Radek Krejcied5acc52019-04-25 15:57:04 +020016
17#include <stdio.h>
18#include <stdlib.h>
19#include <getopt.h>
20#include <errno.h>
21#include <libgen.h>
22#include <sys/stat.h>
23#include <sys/times.h>
24#include <sys/types.h>
25#include <string.h>
26#include <unistd.h>
27
Radek Krejcied5acc52019-04-25 15:57:04 +020028#include "libyang.h"
29
Radek Krejci535ea9f2020-05-29 16:01:05 +020030#include "tools/config.h"
31
32#include "commands.h"
33
34
Radek Krejcied5acc52019-04-25 15:57:04 +020035volatile uint8_t verbose = 0;
36
37#if 0
38/* from commands.c */
39int print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat);
40#endif
41
42void
43help(int shortout)
44{
45 fprintf(stdout, "Usage:\n");
Radek Krejcid8c0f5e2019-11-17 12:18:34 +080046 fprintf(stdout, " yanglint [options] [-f { yang | yin | tree | tree-rfc | info}] <file>...\n");
Radek Krejcied5acc52019-04-25 15:57:04 +020047 fprintf(stdout, " Validates the YANG module in <file>, and all its dependencies.\n\n");
48 fprintf(stdout, " yanglint [options] [-f { xml | json }] <schema>... <file>...\n");
49 fprintf(stdout, " Validates the YANG modeled data in <file> according to the <schema>.\n\n");
50 fprintf(stdout, " yanglint\n");
51 fprintf(stdout, " Starts interactive mode with more features.\n\n");
52
53 if (shortout) {
54 return;
55 }
56 fprintf(stdout, "Options:\n"
57 " -h, --help Show this help message and exit.\n"
58 " -v, --version Show version number and exit.\n"
59 " -V, --verbose Show verbose messages, can be used multiple times to\n"
60 " increase verbosity.\n"
61#ifndef NDEBUG
62 " -G GROUPS, --debug=GROUPS\n"
63 " Enable printing of specific debugging message group\n"
64 " (nothing will be printed unless verbosity is set to debug):\n"
65 " <group>[,<group>]* (dict, yang, yin, xpath, diff)\n\n"
66#endif
67 " -p PATH, --path=PATH Search path for schema (YANG/YIN) modules. The option can be used multiple times.\n"
68 " Current working directory and path of the module being added is used implicitly.\n\n"
69 " -D, --disable-searchdir\n"
70 " Do not implicitly search in CWD for schema modules. If specified a second time,\n"
71 " do not even search the module directory (all modules must be explicitly specified).\n\n"
72 " -s, --strict Strict data parsing (do not skip unknown data),\n"
73 " has no effect for schemas.\n\n"
74 " -m, --merge Merge input data files into a single tree and validate at once,\n"
75 " has no effect for the auto, rpc, rpcreply and notif TYPEs.\n\n"
76 " -f FORMAT, --format=FORMAT\n"
77 " Convert to FORMAT. Supported formats: \n"
Radek Krejcid8c0f5e2019-11-17 12:18:34 +080078 " yang, yin, tree and info for schemas,\n"
Radek Krejcied5acc52019-04-25 15:57:04 +020079 " xml, json for data.\n"
80 " -a, --auto Modify the xml output by adding envelopes for autodetection.\n\n"
81 " -i, --allimplemented Make all the imported modules implemented.\n\n"
82 " -l, --list Print info about the loaded schemas in ietf-yang-library format,\n"
83 " the -f option applies here to specify data encoding.\n"
84 " (i - imported module, I - implemented module)\n\n"
85 " -o OUTFILE, --output=OUTFILE\n"
86 " Write the output to OUTFILE instead of stdout.\n\n"
87 " -F FEATURES, --features=FEATURES\n"
88 " Features to support, default all.\n"
89 " <modname>:[<feature>,]*\n\n"
90 " -d MODE, --default=MODE\n"
91 " Print data with default values, according to the MODE\n"
92 " (to print attributes, ietf-netconf-with-defaults model\n"
93 " must be loaded):\n"
94 " all - Add missing default nodes.\n"
95 " all-tagged - Add missing default nodes and mark all the default\n"
96 " nodes with the attribute.\n"
97 " trim - Remove all nodes with a default value.\n"
98 " implicit-tagged - Add missing nodes and mark them with the attribute.\n\n"
99 " -t TYPE, --type=TYPE\n"
100 " Specify data tree type in the input data file:\n"
101 " auto - Resolve data type (one of the following) automatically\n"
102 " (as pyang does) - applicable only on XML input data.\n"
103 " data - Complete datastore with status data (default type).\n"
104 " config - Configuration datastore (without status data).\n"
105 " get - Result of the NETCONF <get> operation.\n"
106 " getconfig - Result of the NETCONF <get-config> operation.\n"
107 " edit - Content of the NETCONF <edit-config> operation.\n"
108 " rpc - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.\n"
109 " rpcreply - Reply to the RPC. The input data <file>s are expected in pairs - each RPC reply\n"
110 " input data <file> must be followed by the origin RPC input data <file> for the reply.\n"
111 " The same rule of pairing applies also in case of 'auto' TYPE and input data file\n"
112 " containing RPC reply.\n"
113 " notif - Notification instance (content of the <notification> element without <eventTime>.\n\n"
114 " -O FILE, --operational=FILE\n"
115 " - Optional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the FILE contains running\n"
116 " configuration datastore and state data (operational datastore) referenced from\n"
117 " the RPC/Notification. The same data apply to all input data <file>s. Note that\n"
118 " the file is validated as 'data' TYPE. Special value '!' can be used as FILE argument\n"
119 " to ignore the external references.\n\n"
120 " -y YANGLIB_PATH - Path to a yang-library data describing the initial context.\n\n"
121 "Tree output specific options:\n"
122 " --tree-help - Print help on tree symbols and exit.\n"
123 " --tree-print-groupings\n"
124 " Print top-level groupings in a separate section.\n"
125 " --tree-print-uses - Print uses nodes instead the resolved grouping nodes.\n"
126 " --tree-no-leafref-target\n"
127 " Do not print target nodes of leafrefs.\n"
128 " --tree-path=SCHEMA_PATH\n"
129 " Print only the specified subtree.\n"
130 " --tree-line-length=LINE_LENGTH\n"
131 " Wrap lines if longer than the specified length (it is not a strict limit, longer lines\n"
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800132 " can often appear).\n\n"
133 "Info output specific options:\n"
134 " -P INFOPATH, --info-path=INFOPATH\n"
135 " - Schema path with full module names used as node's prefixes, the path identify the root\n"
136 " node of the subtree to print information about.\n"
137 " --single-node - Print information about a single node instead of a subtree."
Radek Krejcied5acc52019-04-25 15:57:04 +0200138 "\n");
139}
140
141void
142tree_help(void)
143{
144 fprintf(stdout, "Each node is printed as:\n\n");
145 fprintf(stdout, "<status> <flags> <name> <opts> <type> <if-features>\n\n"
146 " <status> is one of:\n"
147 " + for current\n"
148 " x for deprecated\n"
149 " o for obsolete\n\n"
150 " <flags> is one of:\n"
151 " rw for configuration data\n"
152 " ro for status data\n"
153 " -x for RPCs\n"
154 " -n for Notification\n\n"
155 " <name> is the name of the node\n"
156 " (<name>) means that the node is a choice node\n"
157 " :(<name>) means that the node is a case node\n\n"
158 " if the node is augmented into the tree from another module,\n"
159 " it is printed with the module name as <module-name>:<name>.\n\n"
160 " <opts> is one of:\n"
161 " ? for an optional leaf or choice\n"
162 " ! for a presence container\n"
163 " * for a leaf-list or list\n"
164 " [<keys>] for a list's keys\n\n"
165 " <type> is the name of the type for leafs and leaf-lists\n"
166 " If there is a default value defined, it is printed within\n"
167 " angle brackets <default-value>.\n"
168 " If the type is a leafref, the type is printed as -> TARGET`\n\n"
169 " <if-features> is the list of features this node depends on,\n"
170 " printed within curly brackets and a question mark {...}?\n\n");
171}
172
173void
174version(void)
175{
176 fprintf(stdout, "yanglint %s\n", PROJECT_VERSION);
177}
178
179void
180libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
181{
182 char *levstr;
183
184 if (level <= verbose) {
185 switch(level) {
186 case LY_LLERR:
187 levstr = "err :";
188 break;
189 case LY_LLWRN:
190 levstr = "warn:";
191 break;
192 case LY_LLVRB:
193 levstr = "verb:";
194 break;
195 default:
196 levstr = "dbg :";
197 break;
198 }
199 if (path) {
200 fprintf(stderr, "%s %s (%s)\n", levstr, msg, path);
201 } else {
202 fprintf(stderr, "%s %s\n", levstr, msg);
203 }
204 }
205}
206
207/*
208 * return:
209 * 0 - error
210 * 1 - schema format
211 * 2 - data format
212 */
213static int
Radek Krejcie7b95092019-05-15 11:03:07 +0200214get_fileformat(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
Radek Krejcied5acc52019-04-25 15:57:04 +0200215{
216 char *ptr;
217 LYS_INFORMAT informat_s;
Radek Krejcied5acc52019-04-25 15:57:04 +0200218 LYD_FORMAT informat_d;
Radek Krejcie7b95092019-05-15 11:03:07 +0200219
Radek Krejcied5acc52019-04-25 15:57:04 +0200220 /* get the file format */
221 if ((ptr = strrchr(filename, '.')) != NULL) {
222 ++ptr;
223 if (!strcmp(ptr, "yang")) {
224 informat_s = LYS_IN_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200225 informat_d = 0;
226 } else if (!strcmp(ptr, "yin")) {
227 informat_s = LYS_IN_YIN;
228 informat_d = 0;
229 } else if (!strcmp(ptr, "xml")) {
230 informat_s = 0;
231 informat_d = LYD_XML;
Radek Krejcie7b95092019-05-15 11:03:07 +0200232#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200233 } else if (!strcmp(ptr, "json")) {
234 informat_s = 0;
235 informat_d = LYD_JSON;
236#endif
237 } else {
238 fprintf(stderr, "yanglint error: input file in an unknown format \"%s\".\n", ptr);
239 return 0;
240 }
241 } else {
242 fprintf(stderr, "yanglint error: input file \"%s\" without file extension - unknown format.\n", filename);
243 return 0;
244 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200245
Radek Krejcied5acc52019-04-25 15:57:04 +0200246 if (data) {
247 (*data) = informat_d;
248 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200249
Radek Krejcied5acc52019-04-25 15:57:04 +0200250 if (schema) {
251 (*schema) = informat_s;
252 }
253
254 if (informat_s) {
255 return 1;
256 } else {
257 return 2;
258 }
259}
260
261int
262main_ni(int argc, char* argv[])
263{
264 int ret = EXIT_FAILURE;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800265 int opt, opt_index = 0, i, featsize = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200266 struct option options[] = {
267#if 0
268 {"auto", no_argument, NULL, 'a'},
269 {"default", required_argument, NULL, 'd'},
270#endif
271 {"format", required_argument, NULL, 'f'},
272 {"features", required_argument, NULL, 'F'},
273#if 0
274 {"tree-print-groupings", no_argument, NULL, 'g'},
275 {"tree-print-uses", no_argument, NULL, 'u'},
276 {"tree-no-leafref-target", no_argument, NULL, 'n'},
277 {"tree-path", required_argument, NULL, 'P'},
278 {"tree-line-length", required_argument, NULL, 'L'},
279#endif
280 {"help", no_argument, NULL, 'h'},
281#if 0
282 {"tree-help", no_argument, NULL, 'H'},
283#endif
284 {"allimplemented", no_argument, NULL, 'i'},
285 {"disable-cwd-search", no_argument, NULL, 'D'},
286 {"list", no_argument, NULL, 'l'},
287#if 0
288 {"merge", no_argument, NULL, 'm'},
289#endif
290 {"output", required_argument, NULL, 'o'},
291 {"path", required_argument, NULL, 'p'},
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800292 {"info-path", required_argument, NULL, 'P'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200293#if 0
294 {"running", required_argument, NULL, 'r'},
295 {"operational", required_argument, NULL, 'O'},
Radek Krejcie7b95092019-05-15 11:03:07 +0200296#endif
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800297 {"single-node", no_argument, NULL, 'q'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200298 {"strict", no_argument, NULL, 's'},
299 {"type", required_argument, NULL, 't'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200300 {"version", no_argument, NULL, 'v'},
301 {"verbose", no_argument, NULL, 'V'},
302#ifndef NDEBUG
303 {"debug", required_argument, NULL, 'G'},
304#endif
305 {NULL, required_argument, NULL, 'y'},
306 {NULL, 0, NULL, 0}
307 };
Radek Krejci241f6b52020-05-21 18:13:49 +0200308 struct ly_out *out = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200309 struct ly_ctx *ctx = NULL;
310 const struct lys_module *mod;
311 LYS_OUTFORMAT outformat_s = 0;
312 LYS_INFORMAT informat_s;
Radek Krejcie7b95092019-05-15 11:03:07 +0200313 LYD_FORMAT informat_d, outformat_d = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200314#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200315 LYD_FORMAT ylformat = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200316#endif
317 struct ly_set *searchpaths = NULL;
318 const char *outtarget_s = NULL;
319 char **feat = NULL, *ptr, *featlist, *dir;
320 struct stat st;
321 uint32_t u;
322 int options_ctx = LY_CTX_NOYANGLIBRARY, list = 0, outoptions_s = 0, outline_length_s = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200323 int autodetection = 0, options_parser = 0, merge = 0;
324 const char *oper_file = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200325#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200326 const char *envelope_s = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200327 char *ylpath = NULL;
Radek Krejcie7b95092019-05-15 11:03:07 +0200328 int options_dflt = 0, envelope = 0;
329 struct lyxml_elem *iter, *elem;
330 struct *subroot, *next, *node;
331#endif
Michal Vaskof03ed032020-03-04 13:31:44 +0100332 struct lyd_node *tree = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200333 struct dataitem {
334 const char *filename;
Radek Krejcied5acc52019-04-25 15:57:04 +0200335 struct lyd_node *tree;
336 struct dataitem *next;
337 LYD_FORMAT format;
Michal Vaskoa3881362020-01-21 15:57:35 +0100338 int flags;
Radek Krejcied5acc52019-04-25 15:57:04 +0200339 } *data = NULL, *data_item, *data_prev = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200340 struct ly_set *mods = NULL;
341 void *p;
342 int index = 0;
343
344 opterr = 0;
345#ifndef NDEBUG
Radek Krejci693262f2019-04-29 15:23:20 +0200346 while ((opt = getopt_long(argc, argv, "acd:f:F:gunP:L:hHiDlmo:p:r:O:st:vVG:y:", options, &opt_index)) != -1)
Radek Krejcied5acc52019-04-25 15:57:04 +0200347#else
Radek Krejci693262f2019-04-29 15:23:20 +0200348 while ((opt = getopt_long(argc, argv, "acd:f:F:gunP:L:hHiDlmo:p:r:O:st:vVy:", options, &opt_index)) != -1)
Radek Krejcied5acc52019-04-25 15:57:04 +0200349#endif
350 {
351 switch (opt) {
352#if 0
353 case 'a':
354 envelope = 1;
355 break;
356 case 'd':
357 if (!strcmp(optarg, "all")) {
358 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL;
359 } else if (!strcmp(optarg, "all-tagged")) {
360 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
361 } else if (!strcmp(optarg, "trim")) {
362 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_TRIM;
363 } else if (!strcmp(optarg, "implicit-tagged")) {
364 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
365 } else {
366 fprintf(stderr, "yanglint error: unknown default mode %s\n", optarg);
367 help(1);
368 goto cleanup;
369 }
370 break;
371#endif
372 case 'f':
373 if (!strcasecmp(optarg, "yang")) {
374 outformat_s = LYS_OUT_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200375 outformat_d = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200376#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200377 } else if (!strcasecmp(optarg, "tree")) {
378 outformat_s = LYS_OUT_TREE;
379 outformat_d = 0;
380 } else if (!strcasecmp(optarg, "tree-rfc")) {
381 outformat_s = LYS_OUT_TREE;
382 outoptions_s |= LYS_OUTOPT_TREE_RFC;
383 outformat_d = 0;
FredGand944bdc2019-11-05 21:57:07 +0800384#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200385 } else if (!strcasecmp(optarg, "yin")) {
386 outformat_s = LYS_OUT_YIN;
387 outformat_d = 0;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800388 } else if (!strcasecmp(optarg, "info")) {
389 outformat_s = LYS_OUT_YANG_COMPILED;
Radek Krejcied5acc52019-04-25 15:57:04 +0200390 outformat_d = 0;
391 } else if (!strcasecmp(optarg, "xml")) {
392 outformat_s = 0;
393 outformat_d = LYD_XML;
Radek Krejcie7b95092019-05-15 11:03:07 +0200394#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200395 } else if (!strcasecmp(optarg, "json")) {
396 outformat_s = 0;
397 outformat_d = LYD_JSON;
398#endif
399 } else {
400 fprintf(stderr, "yanglint error: unknown output format %s\n", optarg);
401 help(1);
402 goto cleanup;
403 }
404 break;
405 case 'F':
406 featsize++;
407 if (!feat) {
408 p = malloc(sizeof *feat);
409 } else {
410 p = realloc(feat, featsize * sizeof *feat);
411 }
412 if (!p) {
413 fprintf(stderr, "yanglint error: Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
414 goto cleanup;
415 }
416 feat = p;
417 feat[featsize - 1] = strdup(optarg);
418 ptr = strchr(feat[featsize - 1], ':');
419 if (!ptr) {
420 fprintf(stderr, "yanglint error: Invalid format of the features specification (%s)", optarg);
421 goto cleanup;
422 }
423 *ptr = '\0';
424
425 break;
426#if 0
427 case 'g':
428 outoptions_s |= LYS_OUTOPT_TREE_GROUPING;
429 break;
430 case 'u':
431 outoptions_s |= LYS_OUTOPT_TREE_USES;
432 break;
433 case 'n':
434 outoptions_s |= LYS_OUTOPT_TREE_NO_LEAFREF;
435 break;
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800436#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200437 case 'P':
438 outtarget_s = optarg;
439 break;
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800440#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200441 case 'L':
442 outline_length_s = atoi(optarg);
443 break;
444#endif
445 case 'h':
446 help(0);
447 ret = EXIT_SUCCESS;
448 goto cleanup;
449#if 0
450 case 'H':
451 tree_help();
452 ret = EXIT_SUCCESS;
453 goto cleanup;
454#endif
455 case 'i':
456 options_ctx |= LY_CTX_ALLIMPLEMENTED;
457 break;
458 case 'D':
459 if (options_ctx & LY_CTX_DISABLE_SEARCHDIRS) {
460 fprintf(stderr, "yanglint error: -D specified too many times.\n");
461 goto cleanup;
462 } else if (options_ctx & LY_CTX_DISABLE_SEARCHDIR_CWD) {
463 options_ctx &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
464 options_ctx |= LY_CTX_DISABLE_SEARCHDIRS;
465 } else {
466 options_ctx |= LY_CTX_DISABLE_SEARCHDIR_CWD;
467 }
468 break;
469 case 'l':
470 list = 1;
471 break;
472#if 0
473 case 'm':
474 merge = 1;
475 break;
476#endif
477 case 'o':
Radek Krejcia5bba312020-01-09 15:41:20 +0100478 if (out) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200479 if (ly_out_filepath(out, optarg) != NULL) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100480 fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
481 goto cleanup;
482 }
483 } else {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200484 if (ly_out_new_filepath(optarg, &out)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100485 fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
486 goto cleanup;
487 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200488 }
489 break;
490 case 'p':
491 if (stat(optarg, &st) == -1) {
492 fprintf(stderr, "yanglint error: Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
493 goto cleanup;
494 }
495 if (!S_ISDIR(st.st_mode)) {
496 fprintf(stderr, "yanglint error: Provided search path is not a directory.\n");
497 goto cleanup;
498 }
499 if (!searchpaths) {
500 searchpaths = ly_set_new();
501 }
502 ly_set_add(searchpaths, optarg, 0);
503 break;
504#if 0
505 case 'r':
506 case 'O':
507 if (oper_file || (options_parser & LYD_OPT_NOEXTDEPS)) {
508 fprintf(stderr, "yanglint error: The operational datastore (-O) cannot be set multiple times.\n");
509 goto cleanup;
510 }
511 if (optarg[0] == '!') {
512 /* ignore extenral dependencies to the operational datastore */
513 options_parser |= LYD_OPT_NOEXTDEPS;
514 } else {
515 /* external file with the operational datastore */
516 oper_file = optarg;
517 }
518 break;
Radek Krejcie7b95092019-05-15 11:03:07 +0200519#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200520 case 's':
Radek Krejci7931b192020-06-25 17:05:03 +0200521 options_parser |= LYD_PARSE_STRICT;
Radek Krejcied5acc52019-04-25 15:57:04 +0200522 break;
523 case 't':
524 if (!strcmp(optarg, "auto")) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200525 autodetection = 1;
Michal Vaskob36053d2020-03-26 15:49:30 +0100526 /*} else if (!strcmp(optarg, "config")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100527 options_parser |= LYD_OPT_CONFIG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200528 } else if (!strcmp(optarg, "get")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100529 options_parser |= LYD_OPT_GET;
Radek Krejcied5acc52019-04-25 15:57:04 +0200530 } else if (!strcmp(optarg, "getconfig")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100531 options_parser |= LYD_OPT_GETCONFIG;
Michal Vaskob36053d2020-03-26 15:49:30 +0100532 } else if (!strcmp(optarg, "edit")) {
Michal Vasko9f96a052020-03-10 09:41:45 +0100533 options_parser |= LYD_OPT_EDIT;*/
Radek Krejcied5acc52019-04-25 15:57:04 +0200534 } else if (!strcmp(optarg, "data")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100535 /* no options */
Radek Krejcied5acc52019-04-25 15:57:04 +0200536 } else {
537 fprintf(stderr, "yanglint error: unknown data tree type %s\n", optarg);
538 help(1);
539 goto cleanup;
540 }
541 break;
Radek Krejcied5acc52019-04-25 15:57:04 +0200542 case 'v':
543 version();
544 ret = EXIT_SUCCESS;
545 goto cleanup;
546 case 'V':
547 verbose++;
548 break;
549#ifndef NDEBUG
550 case 'G':
551 u = 0;
552 ptr = optarg;
553 while (ptr[0]) {
554 if (!strncmp(ptr, "dict", 4)) {
555 u |= LY_LDGDICT;
556 ptr += 4;
557 } else if (!strncmp(ptr, "yang", 4)) {
558 u |= LY_LDGYANG;
559 ptr += 4;
560 } else if (!strncmp(ptr, "yin", 3)) {
561 u |= LY_LDGYIN;
562 ptr += 3;
563 } else if (!strncmp(ptr, "xpath", 5)) {
564 u |= LY_LDGXPATH;
565 ptr += 5;
566 } else if (!strncmp(ptr, "diff", 4)) {
567 u |= LY_LDGDIFF;
568 ptr += 4;
569 }
570
571 if (ptr[0]) {
572 if (ptr[0] != ',') {
573 fprintf(stderr, "yanglint error: unknown debug group string \"%s\"\n", optarg);
574 goto cleanup;
575 }
576 ++ptr;
577 }
578 }
579 ly_verb_dbg(u);
580 break;
581#endif
582#if 0
583 case 'y':
584 ptr = strrchr(optarg, '.');
585 if (ptr) {
586 ptr++;
587 if (!strcmp(ptr, "xml")) {
588 ylformat = LYD_XML;
589 } else if (!strcmp(ptr, "json")) {
590 ylformat = LYD_JSON;
591 } else {
592 fprintf(stderr, "yanglint error: yang-library file in an unknown format \"%s\".\n", ptr);
593 goto cleanup;
594 }
595 } else {
596 fprintf(stderr, "yanglint error: yang-library file in an unknown format.\n");
597 goto cleanup;
598 }
599 ylpath = optarg;
600 break;
601#endif
602 default:
603 help(1);
604 if (optopt) {
605 fprintf(stderr, "yanglint error: invalid option: -%c\n", optopt);
606 } else {
607 fprintf(stderr, "yanglint error: invalid option: %s\n", argv[optind - 1]);
608 }
609 goto cleanup;
610 }
611 }
612
613 /* check options compatibility */
614 if (!list && optind >= argc) {
615 help(1);
616 fprintf(stderr, "yanglint error: missing <file> to process\n");
617 goto cleanup;
618 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200619 if (outformat_s && outformat_s != LYS_OUT_TREE && (optind + 1) < argc) {
620 /* we have multiple schemas to be printed as YIN or YANG */
621 fprintf(stderr, "yanglint error: too many schemas to convert and store.\n");
622 goto cleanup;
623 }
624 if (outoptions_s || outtarget_s || outline_length_s) {
625#if 0
626 if (outformat_d || (outformat_s && outformat_s != LYS_OUT_TREE)) {
627 /* we have --tree-print-grouping with other output format than tree */
628 fprintf(stderr,
629 "yanglint warning: --tree options take effect only in case of the tree output format.\n");
630 }
631 }
632 if (merge) {
633 if (autodetection || (options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF))) {
634 fprintf(stderr, "yanglint warning: merging not allowed, ignoring option -m.\n");
635 merge = 0;
636 } else {
637 /* first, files will be parsed as trusted to allow missing data, then the data trees will be merged
638 * and the result will be validated */
639 options_parser |= LYD_OPT_TRUSTED;
640 }
641#endif
642 }
643#if 0
644 if (!outformat_d && options_dflt) {
645 /* we have options for printing default nodes, but data output not specified */
646 fprintf(stderr, "yanglint warning: default mode is ignored when not printing data.\n");
647 }
648 if (outformat_s && (options_parser || autodetection)) {
649 /* we have options for printing data tree, but output is schema */
650 fprintf(stderr, "yanglint warning: data parser options are ignored when printing schema.\n");
651 }
652 if (oper_file && (!autodetection && !(options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF)))) {
653 fprintf(stderr, "yanglint warning: operational datastore applies only to RPCs or Notifications.\n");
654 /* ignore operational datastore file */
655 oper_file = NULL;
656 }
657 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_DATA) {
658 /* add option to ignore ietf-yang-library data for implicit data type */
659 options_parser |= LYD_OPT_DATA_NO_YANGLIB;
660 }
Michal Vaskoa3881362020-01-21 15:57:35 +0100661#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200662
663 /* set callback for printing libyang messages */
664 ly_set_log_clb(libyang_verbclb, 1);
665#if 0
666 /* create libyang context */
667 if (ylpath) {
668 ctx = ly_ctx_new_ylpath(searchpaths ? (const char*)searchpaths->set.g[0] : NULL, ylpath, ylformat, options_ctx);
669 } else {
670#else
671 {
672#endif
673 ly_ctx_new(NULL, options_ctx, &ctx);
674 }
675 if (!ctx) {
676 goto cleanup;
677 }
678
679 /* set searchpaths */
680 if (searchpaths) {
681 for (u = 0; u < searchpaths->count; u++) {
682 ly_ctx_set_searchdir(ctx, (const char*)searchpaths->objs[u]);
683 }
684 index = u + 1;
685 }
686
687 /* derefered setting of verbosity in libyang after context initiation */
688 ly_verb(verbose);
689
690 mods = ly_set_new();
691
692
693 /* divide input files */
694 for (i = 0; i < argc - optind; i++) {
695 /* get the file format */
Radek Krejcie7b95092019-05-15 11:03:07 +0200696 if (!get_fileformat(argv[optind + i], &informat_s, &informat_d)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200697 goto cleanup;
698 }
699
700 if (informat_s) {
701 /* load/validate schema */
702 if (verbose >= 2) {
703 fprintf(stdout, "Validating %s schema file.\n", argv[optind + i]);
704 }
705 dir = strdup(argv[optind + i]);
706 ly_ctx_set_searchdir(ctx, ptr = dirname(dir));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200707 lys_parse_path(ctx, argv[optind + i], informat_s, &mod);
Radek Krejcied5acc52019-04-25 15:57:04 +0200708 ly_ctx_unset_searchdir(ctx, index);
709 free(dir);
710 if (!mod) {
711 goto cleanup;
712 }
713 ly_set_add(mods, (void *)mod, 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200714 } else {
715 if (autodetection && informat_d != LYD_XML) {
716 /* data file content autodetection is possible only for XML input */
717 fprintf(stderr, "yanglint error: data type autodetection is applicable only to XML files.\n");
718 goto cleanup;
719 }
720
721 /* remember data filename and its format */
722 if (!data) {
723 data = data_item = malloc(sizeof *data);
724 } else {
725 for (data_item = data; data_item->next; data_item = data_item->next);
726 data_item->next = malloc(sizeof *data_item);
727 data_item = data_item->next;
728 }
729 data_item->filename = argv[optind + i];
730 data_item->format = informat_d;
Michal Vaskoa3881362020-01-21 15:57:35 +0100731 data_item->flags = options_parser;
Radek Krejcied5acc52019-04-25 15:57:04 +0200732 data_item->tree = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200733 data_item->next = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200734 }
735 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200736 if (outformat_d && !data && !list) {
737 fprintf(stderr, "yanglint error: no input data file for the specified data output format.\n");
738 goto cleanup;
739 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200740
741 /* enable specified features, if not specified, all the module's features are enabled */
742 u = 4; /* skip internal libyang modules */
743 while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
744 for (i = 0; i < featsize; i++) {
745 if (!strcmp(feat[i], mod->name)) {
746 /* parse features spec */
747 featlist = strdup(feat[i] + strlen(feat[i]) + 1);
748 ptr = NULL;
749 while((ptr = strtok(ptr ? NULL : featlist, ","))) {
750 if (verbose >= 2) {
751 fprintf(stdout, "Enabling feature %s in module %s.\n", ptr, mod->name);
752 }
753 if (lys_feature_enable(mod, ptr)) {
754 fprintf(stderr, "Feature %s not defined in module %s.\n", ptr, mod->name);
755 }
756 }
757 free(featlist);
758 break;
759 }
760 }
761 if (i == featsize) {
762 if (verbose >= 2) {
763 fprintf(stdout, "Enabling all features in module %s.\n", mod->name);
764 }
765 lys_feature_enable(mod, "*");
766 }
767 }
768
769 /* convert (print) to FORMAT */
770 if (outformat_s) {
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800771 if (outtarget_s) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100772 const struct lysc_node *node = lys_find_node(ctx, NULL, outtarget_s);
773 if (node) {
774 lys_print_node(out, node, outformat_s, outline_length_s, outoptions_s);
775 } else {
776 fprintf(stderr, "yanglint error: The requested schema node \"%s\" does not exists.\n", outtarget_s);
777 }
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800778 } else {
779 for (u = 0; u < mods->count; u++) {
780 if (u) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200781 ly_print(out, "\n");
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800782 }
Radek Krejcia5bba312020-01-09 15:41:20 +0100783 lys_print(out, (struct lys_module *)mods->objs[u], outformat_s, outline_length_s, outoptions_s);
Radek Krejcied5acc52019-04-25 15:57:04 +0200784 }
785 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200786 } else if (data) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200787
788 /* prepare operational datastore when specified for RPC/Notification */
789 if (oper_file) {
Radek Krejci7931b192020-06-25 17:05:03 +0200790 struct ly_in *in;
791 tree = NULL;
792
793 if (ly_in_new_filepath(oper_file, 0, &in)) {
794 fprintf(stderr, "yanglint error: Unable to open an operational data file \"%s\".\n", oper_file);
Radek Krejcied5acc52019-04-25 15:57:04 +0200795 goto cleanup;
796 }
Radek Krejci7931b192020-06-25 17:05:03 +0200797 if (lyd_parse_data(ctx, in, 0, LYD_PARSE_ONLY, 0, &tree) || !tree) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200798 fprintf(stderr, "yanglint error: Failed to parse the operational datastore file for RPC/Notification validation.\n");
Radek Krejci7931b192020-06-25 17:05:03 +0200799 ly_in_free(in, 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200800 goto cleanup;
801 }
Radek Krejci7931b192020-06-25 17:05:03 +0200802 ly_in_free(in, 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200803 }
804
805 for (data_item = data, data_prev = NULL; data_item; data_prev = data_item, data_item = data_item->next) {
806 /* parse data file - via LYD_OPT_TRUSTED postpone validation when all data are loaded and merged */
Radek Krejcie7b95092019-05-15 11:03:07 +0200807#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200808 if (autodetection) {
809 /* erase option not covered by LYD_OPT_TYPEMASK, but used according to the type */
810 options_parser &= ~LYD_OPT_DATA_NO_YANGLIB;
811 /* automatically detect data type from the data top level */
812 data_item->xml = lyxml_parse_path(ctx, data_item->filename, 0);
813 if (!data_item->xml) {
814 fprintf(stderr, "yanglint error: parsing XML data for data type autodetection failed.\n");
815 goto cleanup;
816 }
817
818 /* NOTE: namespace is ignored to simplify usage of this feature */
819 if (!strcmp(data_item->xml->name, "data")) {
820 if (verbose >= 2) {
821 fprintf(stdout, "Parsing %s as complete datastore.\n", data_item->filename);
822 }
823 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
824 data_item->type = LYD_OPT_DATA;
825 } else if (!strcmp(data_item->xml->name, "config")) {
826 if (verbose >= 2) {
827 fprintf(stdout, "Parsing %s as config data.\n", data_item->filename);
828 }
829 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
830 data_item->type = LYD_OPT_CONFIG;
831 } else if (!strcmp(data_item->xml->name, "get-reply")) {
832 if (verbose >= 2) {
833 fprintf(stdout, "Parsing %s as <get> reply data.\n", data_item->filename);
834 }
835 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
836 data_item->type = LYD_OPT_GET;
837 } else if (!strcmp(data_item->xml->name, "get-config-reply")) {
838 if (verbose >= 2) {
839 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", data_item->filename);
840 }
841 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
842 data_item->type = LYD_OPT_GETCONFIG;
843 } else if (!strcmp(data_item->xml->name, "edit-config")) {
844 if (verbose >= 2) {
845 fprintf(stdout, "Parsing %s as <edit-config> data.\n", data_item->filename);
846 }
847 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
848 data_item->type = LYD_OPT_EDIT;
849 } else if (!strcmp(data_item->xml->name, "rpc")) {
850 if (verbose >= 2) {
851 fprintf(stdout, "Parsing %s as <rpc> data.\n", data_item->filename);
852 }
853 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
854 data_item->type = LYD_OPT_RPC;
855 } else if (!strcmp(data_item->xml->name, "rpc-reply")) {
856 if (verbose >= 2) {
857 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", data_item->filename);
858 }
859
860 data_item->type = LYD_OPT_RPCREPLY;
861 if (!data_item->next || (data_prev && !data_prev->tree)) {
862 fprintf(stderr, "RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
863 goto cleanup;
864 }
865
866 continue;
867 } else if (!strcmp(data_item->xml->name, "notification")) {
868 if (verbose >= 2) {
869 fprintf(stdout, "Parsing %s as <notification> data.\n", data_item->filename);
870 }
871 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
872 data_item->type = LYD_OPT_NOTIF;
873
874 /* ignore eventTime element if present */
875 while (data_item->xml->child && !strcmp(data_item->xml->child->name, "eventTime")) {
876 lyxml_free(ctx, data_item->xml->child);
877 }
878 } else {
879 fprintf(stderr, "yanglint error: invalid top-level element \"%s\" for data type autodetection.\n",
880 data_item->xml->name);
881 goto cleanup;
882 }
883
884 data_item->tree = lyd_parse_xml(ctx, &data_item->xml->child, options_parser, oper);
885 if (data_prev && data_prev->type == LYD_OPT_RPCREPLY) {
886parse_reply:
887 /* check result of the RPC parsing, we are going to do another parsing in this step */
888 if (ly_errno) {
889 goto cleanup;
890 }
891
892 /* check that we really have RPC for the reply */
893 if (data_item->type != LYD_OPT_RPC) {
894 fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_prev->filename);
895 goto cleanup;
896 }
897
898 if (data_prev->format == LYD_XML) {
899 /* ignore <ok> and <rpc-error> elements if present */
900 u = 0;
901 LY_TREE_FOR_SAFE(data_prev->xml->child, iter, elem) {
902 if (!strcmp(data_prev->xml->child->name, "ok")) {
903 if (u) {
904 /* rpc-error or ok already present */
905 u = 0x8; /* error flag */
906 } else {
907 u = 0x1 | 0x4; /* <ok> flag with lyxml_free() flag */
908 }
909 } else if (!strcmp(data_prev->xml->child->name, "rpc-error")) {
910 if (u && (u & 0x1)) {
911 /* ok already present, rpc-error can be present multiple times */
912 u = 0x8; /* error flag */
913 } else {
914 u = 0x2 | 0x4; /* <rpc-error> flag with lyxml_free() flag */
915 }
916 }
917
918 if (u == 0x8) {
919 fprintf(stderr, "yanglint error: Invalid RPC reply (%s) content.\n", data_prev->filename);
920 goto cleanup;
921 } else if (u & 0x4) {
922 lyxml_free(ctx, data_prev->xml->child);
923 u &= ~0x4; /* unset lyxml_free() flag */
924 }
925 }
926
927 /* finally, parse RPC reply from the previous step */
928 data_prev->tree = lyd_parse_xml(ctx, &data_prev->xml->child,
929 (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, oper);
930 } else { /* LYD_JSON */
931 data_prev->tree = lyd_parse_path(ctx, data_prev->filename, data_item->format,
932 (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, oper);
933 }
934 }
935 } else if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_RPCREPLY) {
936 if (data_prev && !data_prev->tree) {
937 /* now we should have RPC for the preceding RPC reply */
938 data_item->tree = lyd_parse_path(ctx, data_item->filename, data_item->format,
939 (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC, oper);
940 data_item->type = LYD_OPT_RPC;
941 goto parse_reply;
942 } else {
943 /* now we have RPC reply which will be parsed in next step together with its RPC */
944 if (!data_item->next) {
945 fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
946 goto cleanup;
947 }
948 if (data_item->format == LYD_XML) {
949 /* create rpc-reply container to unify handling with autodetection */
950 data_item->xml = calloc(1, sizeof *data_item->xml);
951 if (!data_item->xml) {
952 fprintf(stderr, "yanglint error: Memory allocation failed failed.\n");
953 goto cleanup;
954 }
955 data_item->xml->name = lydict_insert(ctx, "rpc-reply", 9);
956 data_item->xml->prev = data_item->xml;
957 data_item->xml->child = lyxml_parse_path(ctx, data_item->filename, LYXML_PARSE_MULTIROOT | LYXML_PARSE_NOMIXEDCONTENT);
958 if (data_item->xml->child) {
959 data_item->xml->child->parent = data_item->xml;
960 }
961 }
962 continue;
963 }
964 } else {
Radek Krejcie7b95092019-05-15 11:03:07 +0200965#else
966 {
967#endif
Radek Krejci7931b192020-06-25 17:05:03 +0200968 /* TODO optimize use of ly_in in the loop */
969 struct ly_in *in;
970 if (ly_in_new_filepath(data_item->filename, 0, &in)) {
971 fprintf(stderr, "yanglint error: input data file \"%s\".\n", data_item->filename);
972 goto cleanup;
973 }
974 if (lyd_parse_data(ctx, in, 0, options_parser, 0, &data_item->tree)) {
975 fprintf(stderr, "yanglint error: Failed to parse input data file \"%s\".\n", data_item->filename);
976 ly_in_free(in, 0);
977 goto cleanup;
978 }
979 ly_in_free(in, 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200980 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200981#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200982 if (merge && data != data_item) {
983 if (!data->tree) {
984 data->tree = data_item->tree;
985 } else if (data_item->tree) {
986 /* merge results */
987 if (lyd_merge(data->tree, data_item->tree, LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
988 fprintf(stderr, "yanglint error: merging multiple data trees failed.\n");
989 goto cleanup;
990 }
991 }
992 data_item->tree = NULL;
993 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200994#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200995 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200996#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200997 if (merge) {
998 /* validate the merged data tree, do not trust the input, invalidate all the data first */
999 LY_TREE_FOR(data->tree, subroot) {
1000 LY_TREE_DFS_BEGIN(subroot, next, node) {
1001 node->validity = LYD_VAL_OK;
1002 switch (node->schema->nodetype) {
1003 case LYS_LEAFLIST:
1004 case LYS_LEAF:
1005 if (((struct lys_node_leaf *)node->schema)->type.base == LY_TYPE_LEAFREF) {
1006 node->validity |= LYD_VAL_LEAFREF;
1007 }
1008 break;
1009 case LYS_LIST:
1010 node->validity |= LYD_VAL_UNIQUE;
1011 /* falls through */
1012 case LYS_CONTAINER:
1013 case LYS_NOTIF:
1014 case LYS_RPC:
1015 case LYS_ACTION:
1016 node->validity |= LYD_VAL_MAND;
1017 break;
1018 default:
1019 break;
1020 }
1021 LY_TREE_DFS_END(subroot, next, node)
1022 }
1023 }
1024 if (lyd_validate(&data->tree, options_parser & ~LYD_OPT_TRUSTED, ctx)) {
1025 goto cleanup;
1026 }
1027 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001028#endif
Radek Krejcied5acc52019-04-25 15:57:04 +02001029 /* print only if data output format specified */
1030 if (outformat_d) {
1031 for (data_item = data; data_item; data_item = data_item->next) {
1032 if (!merge && verbose >= 2) {
1033 fprintf(stdout, "File %s:\n", data_item->filename);
1034 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001035#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001036 if (outformat_d == LYD_XML && envelope) {
1037 switch (data_item->type) {
1038 case LYD_OPT_DATA:
1039 envelope_s = "data";
1040 break;
1041 case LYD_OPT_CONFIG:
1042 envelope_s = "config";
1043 break;
1044 case LYD_OPT_GET:
1045 envelope_s = "get-reply";
1046 break;
1047 case LYD_OPT_GETCONFIG:
1048 envelope_s = "get-config-reply";
1049 break;
1050 case LYD_OPT_EDIT:
1051 envelope_s = "edit-config";
1052 break;
1053 case LYD_OPT_RPC:
1054 envelope_s = "rpc";
1055 break;
1056 case LYD_OPT_RPCREPLY:
1057 envelope_s = "rpc-reply";
1058 break;
1059 case LYD_OPT_NOTIF:
1060 envelope_s = "notification";
1061 break;
1062 }
1063 fprintf(out, "<%s>\n", envelope_s);
1064 if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
1065 /* action */
1066 fprintf(out, "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">\n");
1067 }
1068 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001069#endif
Michal Vasko3a41dff2020-07-15 14:30:28 +02001070 lyd_print_all(out, data_item->tree, outformat_d, LYD_PRINT_FORMAT /* TODO defaults | options_dflt */);
Radek Krejcie7b95092019-05-15 11:03:07 +02001071#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001072 if (envelope_s) {
1073 if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
1074 fprintf(out, "</action>\n");
1075 }
1076 fprintf(out, "</%s>\n", envelope_s);
1077 }
1078 if (merge) {
1079 /* stop after first item */
1080 break;
1081 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001082#endif
Radek Krejcied5acc52019-04-25 15:57:04 +02001083 }
1084 }
Radek Krejcied5acc52019-04-25 15:57:04 +02001085 }
1086#if 0
1087 if (list) {
1088 print_list(out, ctx, outformat_d);
1089 }
1090#endif
1091
1092 ret = EXIT_SUCCESS;
1093
1094cleanup:
Radek Krejcied5acc52019-04-25 15:57:04 +02001095 ly_set_free(mods, NULL);
1096 ly_set_free(searchpaths, NULL);
1097 for (i = 0; i < featsize; i++) {
1098 free(feat[i]);
1099 }
1100 free(feat);
Radek Krejcied5acc52019-04-25 15:57:04 +02001101 for (; data; data = data_item) {
1102 data_item = data->next;
Radek Krejcie7b95092019-05-15 11:03:07 +02001103 lyd_free_all(data->tree);
Radek Krejcied5acc52019-04-25 15:57:04 +02001104 free(data);
1105 }
Radek Krejcied5acc52019-04-25 15:57:04 +02001106 ly_ctx_destroy(ctx, NULL);
1107
Radek Krejci241f6b52020-05-21 18:13:49 +02001108 ly_out_free(out, NULL, 1);
Radek Krejcied5acc52019-04-25 15:57:04 +02001109 return ret;
1110}