blob: 00abfa36eceafd4c8df83e0393eebfa89e2f180a [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
Radek Krejci47fab892020-11-05 17:02:41 +010017#include <stdint.h>
Radek Krejcied5acc52019-04-25 15:57:04 +020018#include <stdio.h>
19#include <stdlib.h>
20#include <getopt.h>
21#include <errno.h>
22#include <libgen.h>
23#include <sys/stat.h>
Radek Krejcied5acc52019-04-25 15:57:04 +020024#include <string.h>
Radek Krejci47fab892020-11-05 17:02:41 +010025#include <strings.h>
Radek Krejcied5acc52019-04-25 15:57:04 +020026
Radek Krejcied5acc52019-04-25 15:57:04 +020027#include "libyang.h"
28
Radek Krejci535ea9f2020-05-29 16:01:05 +020029#include "tools/config.h"
30
31#include "commands.h"
32
33
Radek Krejcied5acc52019-04-25 15:57:04 +020034volatile uint8_t verbose = 0;
35
36#if 0
37/* from commands.c */
38int print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat);
39#endif
40
41void
42help(int shortout)
43{
44 fprintf(stdout, "Usage:\n");
Radek Krejcid8c0f5e2019-11-17 12:18:34 +080045 fprintf(stdout, " yanglint [options] [-f { yang | yin | tree | tree-rfc | info}] <file>...\n");
Radek Krejcied5acc52019-04-25 15:57:04 +020046 fprintf(stdout, " Validates the YANG module in <file>, and all its dependencies.\n\n");
47 fprintf(stdout, " yanglint [options] [-f { xml | json }] <schema>... <file>...\n");
48 fprintf(stdout, " Validates the YANG modeled data in <file> according to the <schema>.\n\n");
49 fprintf(stdout, " yanglint\n");
50 fprintf(stdout, " Starts interactive mode with more features.\n\n");
51
52 if (shortout) {
53 return;
54 }
55 fprintf(stdout, "Options:\n"
56 " -h, --help Show this help message and exit.\n"
57 " -v, --version Show version number and exit.\n"
58 " -V, --verbose Show verbose messages, can be used multiple times to\n"
59 " increase verbosity.\n"
60#ifndef NDEBUG
61 " -G GROUPS, --debug=GROUPS\n"
62 " Enable printing of specific debugging message group\n"
63 " (nothing will be printed unless verbosity is set to debug):\n"
64 " <group>[,<group>]* (dict, yang, yin, xpath, diff)\n\n"
65#endif
66 " -p PATH, --path=PATH Search path for schema (YANG/YIN) modules. The option can be used multiple times.\n"
67 " Current working directory and path of the module being added is used implicitly.\n\n"
68 " -D, --disable-searchdir\n"
69 " Do not implicitly search in CWD for schema modules. If specified a second time,\n"
70 " do not even search the module directory (all modules must be explicitly specified).\n\n"
71 " -s, --strict Strict data parsing (do not skip unknown data),\n"
72 " has no effect for schemas.\n\n"
73 " -m, --merge Merge input data files into a single tree and validate at once,\n"
74 " has no effect for the auto, rpc, rpcreply and notif TYPEs.\n\n"
75 " -f FORMAT, --format=FORMAT\n"
76 " Convert to FORMAT. Supported formats: \n"
Radek Krejcid8c0f5e2019-11-17 12:18:34 +080077 " yang, yin, tree and info for schemas,\n"
Radek Krejcied5acc52019-04-25 15:57:04 +020078 " xml, json for data.\n"
79 " -a, --auto Modify the xml output by adding envelopes for autodetection.\n\n"
80 " -i, --allimplemented Make all the imported modules implemented.\n\n"
81 " -l, --list Print info about the loaded schemas in ietf-yang-library format,\n"
82 " the -f option applies here to specify data encoding.\n"
83 " (i - imported module, I - implemented module)\n\n"
84 " -o OUTFILE, --output=OUTFILE\n"
85 " Write the output to OUTFILE instead of stdout.\n\n"
86 " -F FEATURES, --features=FEATURES\n"
87 " Features to support, default all.\n"
88 " <modname>:[<feature>,]*\n\n"
89 " -d MODE, --default=MODE\n"
90 " Print data with default values, according to the MODE\n"
91 " (to print attributes, ietf-netconf-with-defaults model\n"
92 " must be loaded):\n"
93 " all - Add missing default nodes.\n"
94 " all-tagged - Add missing default nodes and mark all the default\n"
95 " nodes with the attribute.\n"
96 " trim - Remove all nodes with a default value.\n"
97 " implicit-tagged - Add missing nodes and mark them with the attribute.\n\n"
98 " -t TYPE, --type=TYPE\n"
99 " Specify data tree type in the input data file:\n"
100 " auto - Resolve data type (one of the following) automatically\n"
101 " (as pyang does) - applicable only on XML input data.\n"
102 " data - Complete datastore with status data (default type).\n"
103 " config - Configuration datastore (without status data).\n"
104 " get - Result of the NETCONF <get> operation.\n"
105 " getconfig - Result of the NETCONF <get-config> operation.\n"
106 " edit - Content of the NETCONF <edit-config> operation.\n"
107 " rpc - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.\n"
108 " rpcreply - Reply to the RPC. The input data <file>s are expected in pairs - each RPC reply\n"
109 " input data <file> must be followed by the origin RPC input data <file> for the reply.\n"
110 " The same rule of pairing applies also in case of 'auto' TYPE and input data file\n"
111 " containing RPC reply.\n"
112 " notif - Notification instance (content of the <notification> element without <eventTime>.\n\n"
113 " -O FILE, --operational=FILE\n"
114 " - Optional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the FILE contains running\n"
115 " configuration datastore and state data (operational datastore) referenced from\n"
116 " the RPC/Notification. The same data apply to all input data <file>s. Note that\n"
117 " the file is validated as 'data' TYPE. Special value '!' can be used as FILE argument\n"
118 " to ignore the external references.\n\n"
119 " -y YANGLIB_PATH - Path to a yang-library data describing the initial context.\n\n"
120 "Tree output specific options:\n"
121 " --tree-help - Print help on tree symbols and exit.\n"
122 " --tree-print-groupings\n"
123 " Print top-level groupings in a separate section.\n"
124 " --tree-print-uses - Print uses nodes instead the resolved grouping nodes.\n"
125 " --tree-no-leafref-target\n"
126 " Do not print target nodes of leafrefs.\n"
127 " --tree-path=SCHEMA_PATH\n"
128 " Print only the specified subtree.\n"
129 " --tree-line-length=LINE_LENGTH\n"
130 " Wrap lines if longer than the specified length (it is not a strict limit, longer lines\n"
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800131 " can often appear).\n\n"
132 "Info output specific options:\n"
133 " -P INFOPATH, --info-path=INFOPATH\n"
134 " - Schema path with full module names used as node's prefixes, the path identify the root\n"
135 " node of the subtree to print information about.\n"
136 " --single-node - Print information about a single node instead of a subtree."
Radek Krejcied5acc52019-04-25 15:57:04 +0200137 "\n");
138}
139
140void
141tree_help(void)
142{
143 fprintf(stdout, "Each node is printed as:\n\n");
144 fprintf(stdout, "<status> <flags> <name> <opts> <type> <if-features>\n\n"
145 " <status> is one of:\n"
146 " + for current\n"
147 " x for deprecated\n"
148 " o for obsolete\n\n"
149 " <flags> is one of:\n"
150 " rw for configuration data\n"
151 " ro for status data\n"
152 " -x for RPCs\n"
153 " -n for Notification\n\n"
154 " <name> is the name of the node\n"
155 " (<name>) means that the node is a choice node\n"
156 " :(<name>) means that the node is a case node\n\n"
157 " if the node is augmented into the tree from another module,\n"
158 " it is printed with the module name as <module-name>:<name>.\n\n"
159 " <opts> is one of:\n"
160 " ? for an optional leaf or choice\n"
161 " ! for a presence container\n"
162 " * for a leaf-list or list\n"
163 " [<keys>] for a list's keys\n\n"
164 " <type> is the name of the type for leafs and leaf-lists\n"
165 " If there is a default value defined, it is printed within\n"
166 " angle brackets <default-value>.\n"
167 " If the type is a leafref, the type is printed as -> TARGET`\n\n"
168 " <if-features> is the list of features this node depends on,\n"
169 " printed within curly brackets and a question mark {...}?\n\n");
170}
171
172void
173version(void)
174{
175 fprintf(stdout, "yanglint %s\n", PROJECT_VERSION);
176}
177
178void
179libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
180{
181 char *levstr;
182
183 if (level <= verbose) {
184 switch(level) {
185 case LY_LLERR:
186 levstr = "err :";
187 break;
188 case LY_LLWRN:
189 levstr = "warn:";
190 break;
191 case LY_LLVRB:
192 levstr = "verb:";
193 break;
194 default:
195 levstr = "dbg :";
196 break;
197 }
198 if (path) {
199 fprintf(stderr, "%s %s (%s)\n", levstr, msg, path);
200 } else {
201 fprintf(stderr, "%s %s\n", levstr, msg);
202 }
203 }
204}
205
206/*
207 * return:
208 * 0 - error
209 * 1 - schema format
210 * 2 - data format
211 */
212static int
Radek Krejcie7b95092019-05-15 11:03:07 +0200213get_fileformat(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
Radek Krejcied5acc52019-04-25 15:57:04 +0200214{
215 char *ptr;
216 LYS_INFORMAT informat_s;
Radek Krejcied5acc52019-04-25 15:57:04 +0200217 LYD_FORMAT informat_d;
Radek Krejcie7b95092019-05-15 11:03:07 +0200218
Radek Krejcied5acc52019-04-25 15:57:04 +0200219 /* get the file format */
220 if ((ptr = strrchr(filename, '.')) != NULL) {
221 ++ptr;
222 if (!strcmp(ptr, "yang")) {
223 informat_s = LYS_IN_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200224 informat_d = 0;
225 } else if (!strcmp(ptr, "yin")) {
226 informat_s = LYS_IN_YIN;
227 informat_d = 0;
228 } else if (!strcmp(ptr, "xml")) {
229 informat_s = 0;
230 informat_d = LYD_XML;
231 } else if (!strcmp(ptr, "json")) {
232 informat_s = 0;
233 informat_d = LYD_JSON;
Radek Krejcied5acc52019-04-25 15:57:04 +0200234 } else {
235 fprintf(stderr, "yanglint error: input file in an unknown format \"%s\".\n", ptr);
236 return 0;
237 }
238 } else {
239 fprintf(stderr, "yanglint error: input file \"%s\" without file extension - unknown format.\n", filename);
240 return 0;
241 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200242
Radek Krejcied5acc52019-04-25 15:57:04 +0200243 if (data) {
244 (*data) = informat_d;
245 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200246
Radek Krejcied5acc52019-04-25 15:57:04 +0200247 if (schema) {
248 (*schema) = informat_s;
249 }
250
251 if (informat_s) {
252 return 1;
253 } else {
254 return 2;
255 }
256}
257
258int
259main_ni(int argc, char* argv[])
260{
261 int ret = EXIT_FAILURE;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800262 int opt, opt_index = 0, i, featsize = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200263 struct option options[] = {
264#if 0
265 {"auto", no_argument, NULL, 'a'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200266#endif
Radek Krejciff61c882020-09-03 13:04:18 +0200267 {"default", required_argument, NULL, 'd'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200268 {"format", required_argument, NULL, 'f'},
269 {"features", required_argument, NULL, 'F'},
270#if 0
271 {"tree-print-groupings", no_argument, NULL, 'g'},
272 {"tree-print-uses", no_argument, NULL, 'u'},
273 {"tree-no-leafref-target", no_argument, NULL, 'n'},
274 {"tree-path", required_argument, NULL, 'P'},
275 {"tree-line-length", required_argument, NULL, 'L'},
276#endif
277 {"help", no_argument, NULL, 'h'},
278#if 0
279 {"tree-help", no_argument, NULL, 'H'},
280#endif
281 {"allimplemented", no_argument, NULL, 'i'},
282 {"disable-cwd-search", no_argument, NULL, 'D'},
283 {"list", no_argument, NULL, 'l'},
284#if 0
285 {"merge", no_argument, NULL, 'm'},
286#endif
287 {"output", required_argument, NULL, 'o'},
288 {"path", required_argument, NULL, 'p'},
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800289 {"info-path", required_argument, NULL, 'P'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200290#if 0
291 {"running", required_argument, NULL, 'r'},
292 {"operational", required_argument, NULL, 'O'},
Radek Krejcie7b95092019-05-15 11:03:07 +0200293#endif
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800294 {"single-node", no_argument, NULL, 'q'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200295 {"strict", no_argument, NULL, 's'},
296 {"type", required_argument, NULL, 't'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200297 {"version", no_argument, NULL, 'v'},
298 {"verbose", no_argument, NULL, 'V'},
299#ifndef NDEBUG
300 {"debug", required_argument, NULL, 'G'},
301#endif
302 {NULL, required_argument, NULL, 'y'},
303 {NULL, 0, NULL, 0}
304 };
Radek Krejci241f6b52020-05-21 18:13:49 +0200305 struct ly_out *out = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200306 struct ly_ctx *ctx = NULL;
307 const struct lys_module *mod;
308 LYS_OUTFORMAT outformat_s = 0;
309 LYS_INFORMAT informat_s;
Radek Krejcie7b95092019-05-15 11:03:07 +0200310 LYD_FORMAT informat_d, outformat_d = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200311#if 0
Radek Krejcie7b95092019-05-15 11:03:07 +0200312 LYD_FORMAT ylformat = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200313#endif
314 struct ly_set *searchpaths = NULL;
315 const char *outtarget_s = NULL;
316 char **feat = NULL, *ptr, *featlist, *dir;
317 struct stat st;
318 uint32_t u;
Michal Vasko25d6ad02020-10-22 12:20:22 +0200319 int options_ctx = LY_CTX_NO_YANGLIBRARY, list = 0, outoptions_s = 0, outline_length_s = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200320 int autodetection = 0, options_parser = 0, merge = 0;
321 const char *oper_file = NULL;
Radek Krejciff61c882020-09-03 13:04:18 +0200322 int options_dflt = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200323#if 0
Radek Krejciff61c882020-09-03 13:04:18 +0200324 ly_bool envelope = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200325 const char *envelope_s = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200326 char *ylpath = NULL;
Radek Krejcie7b95092019-05-15 11:03:07 +0200327 struct lyxml_elem *iter, *elem;
328 struct *subroot, *next, *node;
329#endif
Michal Vaskof03ed032020-03-04 13:31:44 +0100330 struct lyd_node *tree = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200331 struct dataitem {
332 const char *filename;
Radek Krejcied5acc52019-04-25 15:57:04 +0200333 struct lyd_node *tree;
334 struct dataitem *next;
335 LYD_FORMAT format;
Michal Vaskoa3881362020-01-21 15:57:35 +0100336 int flags;
Radek Krejcied5acc52019-04-25 15:57:04 +0200337 } *data = NULL, *data_item, *data_prev = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200338 struct ly_set *mods = NULL;
339 void *p;
Radek Krejcied5acc52019-04-25 15:57:04 +0200340
341 opterr = 0;
342#ifndef NDEBUG
Radek Krejci733811f2020-09-04 10:15:10 +0200343 while ((opt = getopt_long(argc, argv, "acd:f:F:gunP:L:hHiDlmo:p:O:st:vVG:y:", options, &opt_index)) != -1)
Radek Krejcied5acc52019-04-25 15:57:04 +0200344#else
Radek Krejci733811f2020-09-04 10:15:10 +0200345 while ((opt = getopt_long(argc, argv, "acd:f:F:gunP:L:hHiDlmo:p:O:st:vVy:", options, &opt_index)) != -1)
Radek Krejcied5acc52019-04-25 15:57:04 +0200346#endif
347 {
348 switch (opt) {
349#if 0
Radek Krejciff61c882020-09-03 13:04:18 +0200350 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200351 case 'a':
352 envelope = 1;
353 break;
Radek Krejciff61c882020-09-03 13:04:18 +0200354#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200355 case 'd':
356 if (!strcmp(optarg, "all")) {
Radek Krejciff61c882020-09-03 13:04:18 +0200357 options_dflt = (options_dflt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200358 } else if (!strcmp(optarg, "all-tagged")) {
Radek Krejciff61c882020-09-03 13:04:18 +0200359 options_dflt = (options_dflt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200360 } else if (!strcmp(optarg, "trim")) {
Radek Krejciff61c882020-09-03 13:04:18 +0200361 options_dflt = (options_dflt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
Radek Krejcied5acc52019-04-25 15:57:04 +0200362 } else if (!strcmp(optarg, "implicit-tagged")) {
Radek Krejciff61c882020-09-03 13:04:18 +0200363 options_dflt = (options_dflt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200364 } else {
365 fprintf(stderr, "yanglint error: unknown default mode %s\n", optarg);
366 help(1);
367 goto cleanup;
368 }
369 break;
Radek Krejcied5acc52019-04-25 15:57:04 +0200370 case 'f':
371 if (!strcasecmp(optarg, "yang")) {
372 outformat_s = LYS_OUT_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200373 outformat_d = 0;
Radek Krejcie7b95092019-05-15 11:03:07 +0200374#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200375 } else if (!strcasecmp(optarg, "tree")) {
376 outformat_s = LYS_OUT_TREE;
377 outformat_d = 0;
378 } else if (!strcasecmp(optarg, "tree-rfc")) {
379 outformat_s = LYS_OUT_TREE;
380 outoptions_s |= LYS_OUTOPT_TREE_RFC;
381 outformat_d = 0;
FredGand944bdc2019-11-05 21:57:07 +0800382#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200383 } else if (!strcasecmp(optarg, "yin")) {
384 outformat_s = LYS_OUT_YIN;
385 outformat_d = 0;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800386 } else if (!strcasecmp(optarg, "info")) {
387 outformat_s = LYS_OUT_YANG_COMPILED;
Radek Krejcied5acc52019-04-25 15:57:04 +0200388 outformat_d = 0;
389 } else if (!strcasecmp(optarg, "xml")) {
390 outformat_s = 0;
391 outformat_d = LYD_XML;
392 } else if (!strcasecmp(optarg, "json")) {
393 outformat_s = 0;
394 outformat_d = LYD_JSON;
Radek Krejcied5acc52019-04-25 15:57:04 +0200395 } else {
396 fprintf(stderr, "yanglint error: unknown output format %s\n", optarg);
397 help(1);
398 goto cleanup;
399 }
400 break;
401 case 'F':
402 featsize++;
403 if (!feat) {
404 p = malloc(sizeof *feat);
405 } else {
406 p = realloc(feat, featsize * sizeof *feat);
407 }
408 if (!p) {
409 fprintf(stderr, "yanglint error: Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
410 goto cleanup;
411 }
412 feat = p;
413 feat[featsize - 1] = strdup(optarg);
414 ptr = strchr(feat[featsize - 1], ':');
415 if (!ptr) {
416 fprintf(stderr, "yanglint error: Invalid format of the features specification (%s)", optarg);
417 goto cleanup;
418 }
419 *ptr = '\0';
420
421 break;
422#if 0
423 case 'g':
424 outoptions_s |= LYS_OUTOPT_TREE_GROUPING;
425 break;
426 case 'u':
427 outoptions_s |= LYS_OUTOPT_TREE_USES;
428 break;
429 case 'n':
430 outoptions_s |= LYS_OUTOPT_TREE_NO_LEAFREF;
431 break;
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800432#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200433 case 'P':
434 outtarget_s = optarg;
435 break;
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800436#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200437 case 'L':
438 outline_length_s = atoi(optarg);
439 break;
440#endif
441 case 'h':
442 help(0);
443 ret = EXIT_SUCCESS;
444 goto cleanup;
445#if 0
446 case 'H':
447 tree_help();
448 ret = EXIT_SUCCESS;
449 goto cleanup;
450#endif
451 case 'i':
Michal Vasko25d6ad02020-10-22 12:20:22 +0200452 options_ctx |= LY_CTX_ALL_IMPLEMENTED;
Radek Krejcied5acc52019-04-25 15:57:04 +0200453 break;
454 case 'D':
455 if (options_ctx & LY_CTX_DISABLE_SEARCHDIRS) {
456 fprintf(stderr, "yanglint error: -D specified too many times.\n");
457 goto cleanup;
458 } else if (options_ctx & LY_CTX_DISABLE_SEARCHDIR_CWD) {
459 options_ctx &= ~LY_CTX_DISABLE_SEARCHDIR_CWD;
460 options_ctx |= LY_CTX_DISABLE_SEARCHDIRS;
461 } else {
462 options_ctx |= LY_CTX_DISABLE_SEARCHDIR_CWD;
463 }
464 break;
465 case 'l':
466 list = 1;
467 break;
468#if 0
469 case 'm':
470 merge = 1;
471 break;
472#endif
473 case 'o':
Radek Krejcia5bba312020-01-09 15:41:20 +0100474 if (out) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200475 if (ly_out_filepath(out, optarg) != NULL) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100476 fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
477 goto cleanup;
478 }
479 } else {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200480 if (ly_out_new_filepath(optarg, &out)) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100481 fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
482 goto cleanup;
483 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200484 }
485 break;
486 case 'p':
487 if (stat(optarg, &st) == -1) {
488 fprintf(stderr, "yanglint error: Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
489 goto cleanup;
490 }
491 if (!S_ISDIR(st.st_mode)) {
492 fprintf(stderr, "yanglint error: Provided search path is not a directory.\n");
493 goto cleanup;
494 }
495 if (!searchpaths) {
Radek Krejciba03a5a2020-08-27 14:40:41 +0200496 if (ly_set_new(&searchpaths)) {
497 fprintf(stderr, "yanglint error: Preparing storage for searchpaths failed.\n");
498 goto cleanup;
499 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200500 }
Radek Krejciba03a5a2020-08-27 14:40:41 +0200501 if (ly_set_add(searchpaths, optarg, 0, NULL)) {
502 fprintf(stderr, "yanglint error: Storing searchpath failed.\n");
503 goto cleanup;
504 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200505 break;
506#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200507 case 'O':
508 if (oper_file || (options_parser & LYD_OPT_NOEXTDEPS)) {
509 fprintf(stderr, "yanglint error: The operational datastore (-O) cannot be set multiple times.\n");
510 goto cleanup;
511 }
512 if (optarg[0] == '!') {
513 /* ignore extenral dependencies to the operational datastore */
514 options_parser |= LYD_OPT_NOEXTDEPS;
515 } else {
516 /* external file with the operational datastore */
517 oper_file = optarg;
518 }
519 break;
Radek Krejcie7b95092019-05-15 11:03:07 +0200520#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200521 case 's':
Radek Krejci7931b192020-06-25 17:05:03 +0200522 options_parser |= LYD_PARSE_STRICT;
Radek Krejcied5acc52019-04-25 15:57:04 +0200523 break;
524 case 't':
525 if (!strcmp(optarg, "auto")) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200526 autodetection = 1;
Michal Vaskob36053d2020-03-26 15:49:30 +0100527 /*} else if (!strcmp(optarg, "config")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100528 options_parser |= LYD_OPT_CONFIG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200529 } else if (!strcmp(optarg, "get")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100530 options_parser |= LYD_OPT_GET;
Radek Krejcied5acc52019-04-25 15:57:04 +0200531 } else if (!strcmp(optarg, "getconfig")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100532 options_parser |= LYD_OPT_GETCONFIG;
Michal Vaskob36053d2020-03-26 15:49:30 +0100533 } else if (!strcmp(optarg, "edit")) {
Michal Vasko9f96a052020-03-10 09:41:45 +0100534 options_parser |= LYD_OPT_EDIT;*/
Radek Krejcied5acc52019-04-25 15:57:04 +0200535 } else if (!strcmp(optarg, "data")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100536 /* no options */
Radek Krejcied5acc52019-04-25 15:57:04 +0200537 } else {
538 fprintf(stderr, "yanglint error: unknown data tree type %s\n", optarg);
539 help(1);
540 goto cleanup;
541 }
542 break;
Radek Krejcied5acc52019-04-25 15:57:04 +0200543 case 'v':
544 version();
545 ret = EXIT_SUCCESS;
546 goto cleanup;
547 case 'V':
548 verbose++;
549 break;
550#ifndef NDEBUG
551 case 'G':
552 u = 0;
553 ptr = optarg;
554 while (ptr[0]) {
555 if (!strncmp(ptr, "dict", 4)) {
556 u |= LY_LDGDICT;
557 ptr += 4;
Radek Krejcied5acc52019-04-25 15:57:04 +0200558 } else if (!strncmp(ptr, "xpath", 5)) {
559 u |= LY_LDGXPATH;
560 ptr += 5;
Radek Krejcied5acc52019-04-25 15:57:04 +0200561 }
562
563 if (ptr[0]) {
564 if (ptr[0] != ',') {
565 fprintf(stderr, "yanglint error: unknown debug group string \"%s\"\n", optarg);
566 goto cleanup;
567 }
568 ++ptr;
569 }
570 }
Radek Krejci68433c92020-10-12 17:03:55 +0200571 ly_log_dbg_groups(u);
Radek Krejcied5acc52019-04-25 15:57:04 +0200572 break;
573#endif
574#if 0
575 case 'y':
576 ptr = strrchr(optarg, '.');
577 if (ptr) {
578 ptr++;
579 if (!strcmp(ptr, "xml")) {
580 ylformat = LYD_XML;
581 } else if (!strcmp(ptr, "json")) {
582 ylformat = LYD_JSON;
583 } else {
584 fprintf(stderr, "yanglint error: yang-library file in an unknown format \"%s\".\n", ptr);
585 goto cleanup;
586 }
587 } else {
588 fprintf(stderr, "yanglint error: yang-library file in an unknown format.\n");
589 goto cleanup;
590 }
591 ylpath = optarg;
592 break;
593#endif
594 default:
595 help(1);
596 if (optopt) {
597 fprintf(stderr, "yanglint error: invalid option: -%c\n", optopt);
598 } else {
599 fprintf(stderr, "yanglint error: invalid option: %s\n", argv[optind - 1]);
600 }
601 goto cleanup;
602 }
603 }
604
605 /* check options compatibility */
606 if (!list && optind >= argc) {
607 help(1);
608 fprintf(stderr, "yanglint error: missing <file> to process\n");
609 goto cleanup;
610 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200611 if (outformat_s && outformat_s != LYS_OUT_TREE && (optind + 1) < argc) {
612 /* we have multiple schemas to be printed as YIN or YANG */
613 fprintf(stderr, "yanglint error: too many schemas to convert and store.\n");
614 goto cleanup;
615 }
616 if (outoptions_s || outtarget_s || outline_length_s) {
617#if 0
618 if (outformat_d || (outformat_s && outformat_s != LYS_OUT_TREE)) {
619 /* we have --tree-print-grouping with other output format than tree */
620 fprintf(stderr,
621 "yanglint warning: --tree options take effect only in case of the tree output format.\n");
622 }
623 }
624 if (merge) {
625 if (autodetection || (options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF))) {
626 fprintf(stderr, "yanglint warning: merging not allowed, ignoring option -m.\n");
627 merge = 0;
628 } else {
629 /* first, files will be parsed as trusted to allow missing data, then the data trees will be merged
630 * and the result will be validated */
631 options_parser |= LYD_OPT_TRUSTED;
632 }
633#endif
634 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200635 if (!outformat_d && options_dflt) {
636 /* we have options for printing default nodes, but data output not specified */
637 fprintf(stderr, "yanglint warning: default mode is ignored when not printing data.\n");
638 }
Radek Krejciff61c882020-09-03 13:04:18 +0200639#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200640 if (outformat_s && (options_parser || autodetection)) {
641 /* we have options for printing data tree, but output is schema */
642 fprintf(stderr, "yanglint warning: data parser options are ignored when printing schema.\n");
643 }
644 if (oper_file && (!autodetection && !(options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF)))) {
645 fprintf(stderr, "yanglint warning: operational datastore applies only to RPCs or Notifications.\n");
646 /* ignore operational datastore file */
647 oper_file = NULL;
648 }
649 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_DATA) {
650 /* add option to ignore ietf-yang-library data for implicit data type */
651 options_parser |= LYD_OPT_DATA_NO_YANGLIB;
652 }
Michal Vaskoa3881362020-01-21 15:57:35 +0100653#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200654
655 /* set callback for printing libyang messages */
656 ly_set_log_clb(libyang_verbclb, 1);
657#if 0
658 /* create libyang context */
659 if (ylpath) {
660 ctx = ly_ctx_new_ylpath(searchpaths ? (const char*)searchpaths->set.g[0] : NULL, ylpath, ylformat, options_ctx);
661 } else {
662#else
663 {
664#endif
665 ly_ctx_new(NULL, options_ctx, &ctx);
666 }
667 if (!ctx) {
668 goto cleanup;
669 }
670
671 /* set searchpaths */
672 if (searchpaths) {
673 for (u = 0; u < searchpaths->count; u++) {
674 ly_ctx_set_searchdir(ctx, (const char*)searchpaths->objs[u]);
675 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200676 }
677
678 /* derefered setting of verbosity in libyang after context initiation */
Radek Krejci52b6d512020-10-12 12:33:17 +0200679 ly_log_level(verbose);
Radek Krejcied5acc52019-04-25 15:57:04 +0200680
Radek Krejciba03a5a2020-08-27 14:40:41 +0200681 if (ly_set_new(&mods)) {
682 fprintf(stderr, "yanglint error: Preparing storage for the parsed modules failed.\n");
683 goto cleanup;
684 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200685
686
687 /* divide input files */
688 for (i = 0; i < argc - optind; i++) {
689 /* get the file format */
Radek Krejcie7b95092019-05-15 11:03:07 +0200690 if (!get_fileformat(argv[optind + i], &informat_s, &informat_d)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200691 goto cleanup;
692 }
693
694 if (informat_s) {
695 /* load/validate schema */
Radek Krejci83126492020-08-15 15:40:04 +0200696 int unset_path = 1;
697
Radek Krejcied5acc52019-04-25 15:57:04 +0200698 if (verbose >= 2) {
699 fprintf(stdout, "Validating %s schema file.\n", argv[optind + i]);
700 }
Radek Krejci83126492020-08-15 15:40:04 +0200701
702 /* add temporarily also the path of the module itself */
Radek Krejcied5acc52019-04-25 15:57:04 +0200703 dir = strdup(argv[optind + i]);
Radek Krejci83126492020-08-15 15:40:04 +0200704 if (ly_ctx_set_searchdir(ctx, ptr = dirname(dir)) == LY_EEXIST) {
705 unset_path = 0;
706 }
Michal Vasko3a41dff2020-07-15 14:30:28 +0200707 lys_parse_path(ctx, argv[optind + i], informat_s, &mod);
Radek Krejcie58f97f2020-08-18 11:45:08 +0200708 ly_ctx_unset_searchdir_last(ctx, unset_path);
Radek Krejcied5acc52019-04-25 15:57:04 +0200709 free(dir);
710 if (!mod) {
711 goto cleanup;
712 }
Radek Krejciba03a5a2020-08-27 14:40:41 +0200713 if (ly_set_add(mods, (void *)mod, 0, NULL)) {
714 fprintf(stderr, "yanglint error: Storing parsed module for further processing failed.\n");
715 goto cleanup;
716 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200717 } else {
718 if (autodetection && informat_d != LYD_XML) {
719 /* data file content autodetection is possible only for XML input */
720 fprintf(stderr, "yanglint error: data type autodetection is applicable only to XML files.\n");
721 goto cleanup;
722 }
723
724 /* remember data filename and its format */
725 if (!data) {
726 data = data_item = malloc(sizeof *data);
727 } else {
728 for (data_item = data; data_item->next; data_item = data_item->next);
729 data_item->next = malloc(sizeof *data_item);
730 data_item = data_item->next;
731 }
732 data_item->filename = argv[optind + i];
733 data_item->format = informat_d;
Michal Vaskoa3881362020-01-21 15:57:35 +0100734 data_item->flags = options_parser;
Radek Krejcied5acc52019-04-25 15:57:04 +0200735 data_item->tree = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200736 data_item->next = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200737 }
738 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200739 if (outformat_d && !data && !list) {
740 fprintf(stderr, "yanglint error: no input data file for the specified data output format.\n");
741 goto cleanup;
742 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200743
744 /* enable specified features, if not specified, all the module's features are enabled */
745 u = 4; /* skip internal libyang modules */
746 while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
Radek Krejci3d46f612020-08-13 12:01:53 +0200747 if (!mod->implemented) {
748 continue;
749 }
750
Radek Krejcied5acc52019-04-25 15:57:04 +0200751 for (i = 0; i < featsize; i++) {
752 if (!strcmp(feat[i], mod->name)) {
753 /* parse features spec */
754 featlist = strdup(feat[i] + strlen(feat[i]) + 1);
755 ptr = NULL;
756 while((ptr = strtok(ptr ? NULL : featlist, ","))) {
757 if (verbose >= 2) {
758 fprintf(stdout, "Enabling feature %s in module %s.\n", ptr, mod->name);
759 }
Michal Vasko7b1ad1a2020-11-02 15:41:27 +0100760 /*if (lys_feature_enable(mod, ptr)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200761 fprintf(stderr, "Feature %s not defined in module %s.\n", ptr, mod->name);
Michal Vasko7b1ad1a2020-11-02 15:41:27 +0100762 }*/
Radek Krejcied5acc52019-04-25 15:57:04 +0200763 }
764 free(featlist);
765 break;
766 }
767 }
768 if (i == featsize) {
769 if (verbose >= 2) {
770 fprintf(stdout, "Enabling all features in module %s.\n", mod->name);
771 }
Michal Vasko7b1ad1a2020-11-02 15:41:27 +0100772 //lys_feature_enable(mod, "*");
Radek Krejcied5acc52019-04-25 15:57:04 +0200773 }
774 }
Radek Krejci460937d2020-09-04 10:15:36 +0200775 if (!out && (outformat_s || data)) {
776 ly_out_new_file(stdout, &out);
777 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200778 /* convert (print) to FORMAT */
779 if (outformat_s) {
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800780 if (outtarget_s) {
Radek Krejcibc5644c2020-10-27 14:53:17 +0100781 const struct lysc_node *node = lys_find_path(ctx, NULL, outtarget_s, 0);
Radek Krejcia5bba312020-01-09 15:41:20 +0100782 if (node) {
783 lys_print_node(out, node, outformat_s, outline_length_s, outoptions_s);
784 } else {
785 fprintf(stderr, "yanglint error: The requested schema node \"%s\" does not exists.\n", outtarget_s);
786 }
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800787 } else {
788 for (u = 0; u < mods->count; u++) {
789 if (u) {
Radek Krejci241f6b52020-05-21 18:13:49 +0200790 ly_print(out, "\n");
Radek Krejcie1f6d5a2019-11-17 14:03:04 +0800791 }
Michal Vasko7c8439f2020-08-05 13:25:19 +0200792 lys_print_module(out, (struct lys_module *)mods->objs[u], outformat_s, outline_length_s, outoptions_s);
Radek Krejcied5acc52019-04-25 15:57:04 +0200793 }
794 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200795 } else if (data) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200796
797 /* prepare operational datastore when specified for RPC/Notification */
798 if (oper_file) {
Radek Krejci7931b192020-06-25 17:05:03 +0200799 struct ly_in *in;
800 tree = NULL;
801
802 if (ly_in_new_filepath(oper_file, 0, &in)) {
803 fprintf(stderr, "yanglint error: Unable to open an operational data file \"%s\".\n", oper_file);
Radek Krejcied5acc52019-04-25 15:57:04 +0200804 goto cleanup;
805 }
Radek Krejci7931b192020-06-25 17:05:03 +0200806 if (lyd_parse_data(ctx, in, 0, LYD_PARSE_ONLY, 0, &tree) || !tree) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200807 fprintf(stderr, "yanglint error: Failed to parse the operational datastore file for RPC/Notification validation.\n");
Radek Krejci7931b192020-06-25 17:05:03 +0200808 ly_in_free(in, 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200809 goto cleanup;
810 }
Radek Krejci7931b192020-06-25 17:05:03 +0200811 ly_in_free(in, 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200812 }
813
814 for (data_item = data, data_prev = NULL; data_item; data_prev = data_item, data_item = data_item->next) {
815 /* parse data file - via LYD_OPT_TRUSTED postpone validation when all data are loaded and merged */
Radek Krejcie7b95092019-05-15 11:03:07 +0200816#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200817 if (autodetection) {
818 /* erase option not covered by LYD_OPT_TYPEMASK, but used according to the type */
819 options_parser &= ~LYD_OPT_DATA_NO_YANGLIB;
820 /* automatically detect data type from the data top level */
821 data_item->xml = lyxml_parse_path(ctx, data_item->filename, 0);
822 if (!data_item->xml) {
823 fprintf(stderr, "yanglint error: parsing XML data for data type autodetection failed.\n");
824 goto cleanup;
825 }
826
827 /* NOTE: namespace is ignored to simplify usage of this feature */
828 if (!strcmp(data_item->xml->name, "data")) {
829 if (verbose >= 2) {
830 fprintf(stdout, "Parsing %s as complete datastore.\n", data_item->filename);
831 }
832 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
833 data_item->type = LYD_OPT_DATA;
834 } else if (!strcmp(data_item->xml->name, "config")) {
835 if (verbose >= 2) {
836 fprintf(stdout, "Parsing %s as config data.\n", data_item->filename);
837 }
838 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
839 data_item->type = LYD_OPT_CONFIG;
840 } else if (!strcmp(data_item->xml->name, "get-reply")) {
841 if (verbose >= 2) {
842 fprintf(stdout, "Parsing %s as <get> reply data.\n", data_item->filename);
843 }
844 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
845 data_item->type = LYD_OPT_GET;
846 } else if (!strcmp(data_item->xml->name, "get-config-reply")) {
847 if (verbose >= 2) {
848 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", data_item->filename);
849 }
850 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
851 data_item->type = LYD_OPT_GETCONFIG;
852 } else if (!strcmp(data_item->xml->name, "edit-config")) {
853 if (verbose >= 2) {
854 fprintf(stdout, "Parsing %s as <edit-config> data.\n", data_item->filename);
855 }
856 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
857 data_item->type = LYD_OPT_EDIT;
858 } else if (!strcmp(data_item->xml->name, "rpc")) {
859 if (verbose >= 2) {
860 fprintf(stdout, "Parsing %s as <rpc> data.\n", data_item->filename);
861 }
862 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
863 data_item->type = LYD_OPT_RPC;
864 } else if (!strcmp(data_item->xml->name, "rpc-reply")) {
865 if (verbose >= 2) {
866 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", data_item->filename);
867 }
868
869 data_item->type = LYD_OPT_RPCREPLY;
870 if (!data_item->next || (data_prev && !data_prev->tree)) {
871 fprintf(stderr, "RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
872 goto cleanup;
873 }
874
875 continue;
876 } else if (!strcmp(data_item->xml->name, "notification")) {
877 if (verbose >= 2) {
878 fprintf(stdout, "Parsing %s as <notification> data.\n", data_item->filename);
879 }
880 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
881 data_item->type = LYD_OPT_NOTIF;
882
883 /* ignore eventTime element if present */
884 while (data_item->xml->child && !strcmp(data_item->xml->child->name, "eventTime")) {
885 lyxml_free(ctx, data_item->xml->child);
886 }
887 } else {
888 fprintf(stderr, "yanglint error: invalid top-level element \"%s\" for data type autodetection.\n",
889 data_item->xml->name);
890 goto cleanup;
891 }
892
893 data_item->tree = lyd_parse_xml(ctx, &data_item->xml->child, options_parser, oper);
894 if (data_prev && data_prev->type == LYD_OPT_RPCREPLY) {
895parse_reply:
896 /* check result of the RPC parsing, we are going to do another parsing in this step */
897 if (ly_errno) {
898 goto cleanup;
899 }
900
901 /* check that we really have RPC for the reply */
902 if (data_item->type != LYD_OPT_RPC) {
903 fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_prev->filename);
904 goto cleanup;
905 }
906
907 if (data_prev->format == LYD_XML) {
908 /* ignore <ok> and <rpc-error> elements if present */
909 u = 0;
910 LY_TREE_FOR_SAFE(data_prev->xml->child, iter, elem) {
911 if (!strcmp(data_prev->xml->child->name, "ok")) {
912 if (u) {
913 /* rpc-error or ok already present */
914 u = 0x8; /* error flag */
915 } else {
916 u = 0x1 | 0x4; /* <ok> flag with lyxml_free() flag */
917 }
918 } else if (!strcmp(data_prev->xml->child->name, "rpc-error")) {
919 if (u && (u & 0x1)) {
920 /* ok already present, rpc-error can be present multiple times */
921 u = 0x8; /* error flag */
922 } else {
923 u = 0x2 | 0x4; /* <rpc-error> flag with lyxml_free() flag */
924 }
925 }
926
927 if (u == 0x8) {
928 fprintf(stderr, "yanglint error: Invalid RPC reply (%s) content.\n", data_prev->filename);
929 goto cleanup;
930 } else if (u & 0x4) {
931 lyxml_free(ctx, data_prev->xml->child);
932 u &= ~0x4; /* unset lyxml_free() flag */
933 }
934 }
935
936 /* finally, parse RPC reply from the previous step */
937 data_prev->tree = lyd_parse_xml(ctx, &data_prev->xml->child,
938 (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, oper);
939 } else { /* LYD_JSON */
940 data_prev->tree = lyd_parse_path(ctx, data_prev->filename, data_item->format,
941 (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, oper);
942 }
943 }
944 } else if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_RPCREPLY) {
945 if (data_prev && !data_prev->tree) {
946 /* now we should have RPC for the preceding RPC reply */
947 data_item->tree = lyd_parse_path(ctx, data_item->filename, data_item->format,
948 (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC, oper);
949 data_item->type = LYD_OPT_RPC;
950 goto parse_reply;
951 } else {
952 /* now we have RPC reply which will be parsed in next step together with its RPC */
953 if (!data_item->next) {
954 fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
955 goto cleanup;
956 }
957 if (data_item->format == LYD_XML) {
958 /* create rpc-reply container to unify handling with autodetection */
959 data_item->xml = calloc(1, sizeof *data_item->xml);
960 if (!data_item->xml) {
961 fprintf(stderr, "yanglint error: Memory allocation failed failed.\n");
962 goto cleanup;
963 }
964 data_item->xml->name = lydict_insert(ctx, "rpc-reply", 9);
965 data_item->xml->prev = data_item->xml;
966 data_item->xml->child = lyxml_parse_path(ctx, data_item->filename, LYXML_PARSE_MULTIROOT | LYXML_PARSE_NOMIXEDCONTENT);
967 if (data_item->xml->child) {
968 data_item->xml->child->parent = data_item->xml;
969 }
970 }
971 continue;
972 }
973 } else {
Radek Krejcie7b95092019-05-15 11:03:07 +0200974#else
975 {
976#endif
Radek Krejci7931b192020-06-25 17:05:03 +0200977 /* TODO optimize use of ly_in in the loop */
978 struct ly_in *in;
979 if (ly_in_new_filepath(data_item->filename, 0, &in)) {
980 fprintf(stderr, "yanglint error: input data file \"%s\".\n", data_item->filename);
981 goto cleanup;
982 }
Radek Krejci5536d282020-08-04 23:27:44 +0200983 if (lyd_parse_data(ctx, in, 0, options_parser, LYD_VALIDATE_PRESENT, &data_item->tree)) {
Radek Krejci7931b192020-06-25 17:05:03 +0200984 fprintf(stderr, "yanglint error: Failed to parse input data file \"%s\".\n", data_item->filename);
985 ly_in_free(in, 0);
986 goto cleanup;
987 }
988 ly_in_free(in, 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200989 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200990#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200991 if (merge && data != data_item) {
992 if (!data->tree) {
993 data->tree = data_item->tree;
994 } else if (data_item->tree) {
995 /* merge results */
996 if (lyd_merge(data->tree, data_item->tree, LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
997 fprintf(stderr, "yanglint error: merging multiple data trees failed.\n");
998 goto cleanup;
999 }
1000 }
1001 data_item->tree = NULL;
1002 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001003#endif
Radek Krejcied5acc52019-04-25 15:57:04 +02001004 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001005#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001006 if (merge) {
1007 /* validate the merged data tree, do not trust the input, invalidate all the data first */
1008 LY_TREE_FOR(data->tree, subroot) {
1009 LY_TREE_DFS_BEGIN(subroot, next, node) {
1010 node->validity = LYD_VAL_OK;
1011 switch (node->schema->nodetype) {
1012 case LYS_LEAFLIST:
1013 case LYS_LEAF:
1014 if (((struct lys_node_leaf *)node->schema)->type.base == LY_TYPE_LEAFREF) {
1015 node->validity |= LYD_VAL_LEAFREF;
1016 }
1017 break;
1018 case LYS_LIST:
1019 node->validity |= LYD_VAL_UNIQUE;
1020 /* falls through */
1021 case LYS_CONTAINER:
1022 case LYS_NOTIF:
1023 case LYS_RPC:
1024 case LYS_ACTION:
1025 node->validity |= LYD_VAL_MAND;
1026 break;
1027 default:
1028 break;
1029 }
1030 LY_TREE_DFS_END(subroot, next, node)
1031 }
1032 }
1033 if (lyd_validate(&data->tree, options_parser & ~LYD_OPT_TRUSTED, ctx)) {
1034 goto cleanup;
1035 }
1036 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001037#endif
Radek Krejcied5acc52019-04-25 15:57:04 +02001038 /* print only if data output format specified */
1039 if (outformat_d) {
1040 for (data_item = data; data_item; data_item = data_item->next) {
1041 if (!merge && verbose >= 2) {
1042 fprintf(stdout, "File %s:\n", data_item->filename);
1043 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001044#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001045 if (outformat_d == LYD_XML && envelope) {
1046 switch (data_item->type) {
1047 case LYD_OPT_DATA:
1048 envelope_s = "data";
1049 break;
1050 case LYD_OPT_CONFIG:
1051 envelope_s = "config";
1052 break;
1053 case LYD_OPT_GET:
1054 envelope_s = "get-reply";
1055 break;
1056 case LYD_OPT_GETCONFIG:
1057 envelope_s = "get-config-reply";
1058 break;
1059 case LYD_OPT_EDIT:
1060 envelope_s = "edit-config";
1061 break;
1062 case LYD_OPT_RPC:
1063 envelope_s = "rpc";
1064 break;
1065 case LYD_OPT_RPCREPLY:
1066 envelope_s = "rpc-reply";
1067 break;
1068 case LYD_OPT_NOTIF:
1069 envelope_s = "notification";
1070 break;
1071 }
1072 fprintf(out, "<%s>\n", envelope_s);
1073 if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
1074 /* action */
1075 fprintf(out, "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">\n");
1076 }
1077 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001078#endif
Radek Krejciff61c882020-09-03 13:04:18 +02001079 lyd_print_all(out, data_item->tree, outformat_d, options_dflt);
Radek Krejcie7b95092019-05-15 11:03:07 +02001080#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001081 if (envelope_s) {
1082 if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
1083 fprintf(out, "</action>\n");
1084 }
1085 fprintf(out, "</%s>\n", envelope_s);
1086 }
1087 if (merge) {
1088 /* stop after first item */
1089 break;
1090 }
Radek Krejcie7b95092019-05-15 11:03:07 +02001091#endif
Radek Krejcied5acc52019-04-25 15:57:04 +02001092 }
1093 }
Radek Krejcied5acc52019-04-25 15:57:04 +02001094 }
1095#if 0
1096 if (list) {
1097 print_list(out, ctx, outformat_d);
1098 }
1099#endif
1100
1101 ret = EXIT_SUCCESS;
1102
1103cleanup:
Radek Krejcied5acc52019-04-25 15:57:04 +02001104 ly_set_free(mods, NULL);
1105 ly_set_free(searchpaths, NULL);
1106 for (i = 0; i < featsize; i++) {
1107 free(feat[i]);
1108 }
1109 free(feat);
Radek Krejcied5acc52019-04-25 15:57:04 +02001110 for (; data; data = data_item) {
1111 data_item = data->next;
Radek Krejcie7b95092019-05-15 11:03:07 +02001112 lyd_free_all(data->tree);
Radek Krejcied5acc52019-04-25 15:57:04 +02001113 free(data);
1114 }
Radek Krejcied5acc52019-04-25 15:57:04 +02001115 ly_ctx_destroy(ctx, NULL);
1116
Radek Krejci241f6b52020-05-21 18:13:49 +02001117 ly_out_free(out, NULL, 1);
Radek Krejcied5acc52019-04-25 15:57:04 +02001118 return ret;
1119}