blob: 6bd233c422bc782ff39bfd46fa87af64ffe54745 [file] [log] [blame]
Radek Krejci6ef5efc2016-10-10 16:38:49 +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-2016 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
16#include <stdio.h>
17#include <stdlib.h>
18#include <getopt.h>
19#include <errno.h>
Radek Krejcie02fb702017-06-13 16:53:22 +020020#include <libgen.h>
Radek Krejci6ef5efc2016-10-10 16:38:49 +020021#include <sys/stat.h>
22#include <sys/times.h>
23#include <sys/types.h>
24#include <string.h>
25#include <unistd.h>
26
27#include "commands.h"
Jan Kundrátfd7a5c72017-10-26 17:45:06 +020028#include "libyang.h"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020029
30volatile int verbose = 0;
31
Radek Krejci7055a772017-08-08 10:45:30 +020032/* from commands.c */
33int print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat);
34
Radek Krejci6ef5efc2016-10-10 16:38:49 +020035void
36help(int shortout)
37{
Radek Krejcifeb903c2016-10-27 17:29:07 +020038 fprintf(stdout, "Usage:\n");
Michal Vasko568b1952018-01-30 15:53:30 +010039 fprintf(stdout, " yanglint [options] [-f { yang | yin | tree | tree-rfc }] <file>...\n");
Radek Krejcifeb903c2016-10-27 17:29:07 +020040 fprintf(stdout, " Validates the YANG module in <file>, and all its dependencies.\n\n");
41 fprintf(stdout, " yanglint [options] [-f { xml | json }] <schema>... <file>...\n");
42 fprintf(stdout, " Validates the YANG modeled data in <file> according to the <schema>.\n\n");
43 fprintf(stdout, " yanglint\n");
44 fprintf(stdout, " Starts interactive mode with more features.\n\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +020045
46 if (shortout) {
47 return;
48 }
49 fprintf(stdout, "Options:\n"
50 " -h, --help Show this help message and exit.\n"
51 " -v, --version Show version number and exit.\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020052 " -V, --verbose Show verbose messages, can be used multiple times to\n"
53 " increase verbosity.\n"
Michal Vasko5e5172b2017-04-04 11:00:40 +020054#ifndef NDEBUG
Michal Vasko1c8edc72017-04-03 13:49:51 +020055 " -G GROUPS, --debug=GROUPS\n"
56 " Enable printing of specific debugging message group\n"
57 " (nothing will be printed unless verbosity is set to debug):\n"
58 " <group>[,<group>]* (dict, yang, yin, xpath, diff)\n\n"
Michal Vasko5e5172b2017-04-04 11:00:40 +020059#endif
Radek Krejcie02fb702017-06-13 16:53:22 +020060 " -p PATH, --path=PATH Search path for schema (YANG/YIN) modules. The option can be used multiple times.\n"
61 " Current working directory and path of the module being added is used implicitly.\n\n"
Radek Krejci65b372b2017-03-10 11:00:10 +010062 " -s, --strict Strict data parsing (do not skip unknown data),\n"
63 " has no effect for schemas.\n\n"
Radek Krejci7fa15692017-08-08 10:16:00 +020064 " -m, --merge Merge input data files into a single tree and validate at once,\n"
65 " has no effect for the auto, rpc, rpcreply and notif TYPEs.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020066 " -f FORMAT, --format=FORMAT\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020067 " Convert to FORMAT. Supported formats: \n"
68 " tree, yin, yang for schemas,\n"
Radek Krejci922ba5a2017-08-03 11:39:00 +020069 " xml, json for data.\n"
70 " -a, --auto Modify the xml output by adding envelopes for autodetection.\n\n"
Radek Krejci819dd4b2017-03-07 15:35:48 +010071 " -i, --allimplemented Make all the imported modules implemented.\n\n"
Radek Krejci7055a772017-08-08 10:45:30 +020072 " -l, --list Print info about the loaded schemas in ietf-yang-library format,\n"
73 " the -f option applies here to specify data encoding.\n"
74 " (i - imported module, I - implemented module)\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020075 " -o OUTFILE, --output=OUTFILE\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020076 " Write the output to OUTFILE instead of stdout.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020077 " -F FEATURES, --features=FEATURES\n"
78 " Features to support, default all.\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020079 " <modname>:[<feature>,]*\n\n"
80 " -d MODE, --default=MODE\n"
81 " Print data with default values, according to the MODE\n"
82 " (to print attributes, ietf-netconf-with-defaults model\n"
83 " must be loaded):\n"
84 " all - Add missing default nodes.\n"
85 " all-tagged - Add missing default nodes and mark all the default\n"
86 " nodes with the attribute.\n"
87 " trim - Remove all nodes with a default value.\n"
88 " implicit-tagged - Add missing nodes and mark them with the attribute.\n\n"
89 " -t TYPE, --type=TYPE\n"
90 " Specify data tree type in the input data file:\n"
91 " auto - Resolve data type (one of the following) automatically\n"
92 " (as pyang does) - applicable only on XML input data.\n"
93 " data - Complete datastore with status data (default type).\n"
94 " config - Configuration datastore (without status data).\n"
95 " get - Result of the NETCONF <get> operation.\n"
96 " getconfig - Result of the NETCONF <get-config> operation.\n"
Radek Krejci2d296ce2017-08-02 10:40:39 +020097 " edit - Content of the NETCONF <edit-config> operation.\n"
98 " rpc - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.\n"
Radek Krejci27105402017-08-03 15:43:12 +020099 " rpcreply - Reply to the RPC. The input data <file>s are expected in pairs - each RPC reply\n"
Radek Krejci9a189c32017-10-27 14:46:42 +0200100 " input data <file> must be followed by the origin RPC input data <file> for the reply.\n"
Radek Krejci27105402017-08-03 15:43:12 +0200101 " The same rule of pairing applies also in case of 'auto' TYPE and input data file\n"
102 " containing RPC reply.\n"
Radek Krejci2d296ce2017-08-02 10:40:39 +0200103 " notif - Notification instance (content of the <notification> element without <eventTime>.\n\n"
Radek Krejci27105402017-08-03 15:43:12 +0200104 " -r FILE, --running=FILE\n"
105 " - Optional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the FILE contains running\n"
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200106 " configuration datastore data referenced from the RPC/Notification. The same data\n"
Radek Krejcia1ec4d12017-08-03 14:01:06 +0200107 " apply to all input data <file>s. Note that the file is validated as 'data' TYPE.\n"
Radek Krejci27105402017-08-03 15:43:12 +0200108 " Special value '!' can be used as FILE argument to ignore the external references.\n\n"
Radek Krejci1770f612017-03-17 16:34:07 +0100109 " -y YANGLIB_PATH - Path to a yang-library data describing the initial context.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200110 "Tree output specific options:\n"
Michal Vasko568b1952018-01-30 15:53:30 +0100111 " --tree-help - Print help on tree symbols and exit.\n"
Radek Krejcif9c28262017-01-25 11:36:02 +0100112 " --tree-print-groupings\n"
Michal Vasko568b1952018-01-30 15:53:30 +0100113 " Print top-level groupings in a separate section.\n"
114 " --tree-print-uses - Print uses nodes instead the resolved grouping nodes.\n"
115 " --tree-no-leafref-target\n"
116 " Do not print target nodes of leafrefs.\n"
117 " --tree-path=SCHEMA_PATH\n"
118 " Print only the specified subtree.\n"
119 " --tree-line-length=LINE_LENGTH\n"
120 " Wrap lines if longer than the specified length (it is not a strict limit, longer lines\n"
121 " can often appear).\n"
122 "\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200123}
124
125void
126tree_help(void)
127{
128 fprintf(stdout, "Each node is printed as:\n\n");
129 fprintf(stdout, "<status> <flags> <name> <opts> <type> <if-features>\n\n"
130 " <status> is one of:\n"
131 " + for current\n"
132 " x for deprecated\n"
133 " o for obsolete\n\n"
134 " <flags> is one of:\n"
135 " rw for configuration data\n"
136 " ro for status data\n"
137 " -x for RPCs\n"
138 " -n for Notification\n\n"
139 " <name> is the name of the node\n"
140 " (<name>) means that the node is a choice node\n"
141 " :(<name>) means that the node is a case node\n\n"
142 " if the node is augmented into the tree from another module,\n"
143 " it is printed with the module name as <module-name>:<name>.\n\n"
144 " <opts> is one of:\n"
145 " ? for an optional leaf or choice\n"
146 " ! for a presence container\n"
147 " * for a leaf-list or list\n"
148 " [<keys>] for a list's keys\n\n"
149 " <type> is the name of the type for leafs and leaf-lists\n"
150 " If there is a default value defined, it is printed within\n"
151 " angle brackets <default-value>.\n"
152 " If the type is a leafref, the type is printed as -> TARGET`\n\n"
153 " <if-features> is the list of features this node depends on,\n"
154 " printed within curly brackets and a question mark {...}?\n\n");
155}
156
157void
158version(void)
159{
160 fprintf(stdout, "yanglint %d.%d.%d\n", LY_VERSION_MAJOR, LY_VERSION_MINOR, LY_VERSION_MICRO);
161}
162void
163libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
164{
Radek Krejciff5e33f2016-10-25 14:24:26 +0200165 char *levstr;
166
167 if (level <= verbose) {
168 switch(level) {
169 case LY_LLERR:
170 levstr = "err :";
171 break;
172 case LY_LLWRN:
173 levstr = "warn:";
174 break;
175 case LY_LLVRB:
176 levstr = "verb:";
177 break;
178 default:
179 levstr = "dbg :";
180 break;
181 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200182 if (path) {
Radek Krejciff5e33f2016-10-25 14:24:26 +0200183 fprintf(stderr, "%s %s (%s)\n", levstr, msg, path);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200184 } else {
Radek Krejciff5e33f2016-10-25 14:24:26 +0200185 fprintf(stderr, "%s %s\n", levstr, msg);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200186 }
187 }
188}
189
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200190/*
191 * return:
192 * 0 - error
193 * 1 - schema format
194 * 2 - data format
195 */
196static int
197get_fileformat(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
198{
199 char *ptr;
200 LYS_INFORMAT informat_s;
201 LYD_FORMAT informat_d;
202
203 /* get the file format */
204 if ((ptr = strrchr(filename, '.')) != NULL) {
205 ++ptr;
206 if (!strcmp(ptr, "yin")) {
207 informat_s = LYS_IN_YIN;
208 informat_d = 0;
209 } else if (!strcmp(ptr, "yang")) {
210 informat_s = LYS_IN_YANG;
211 informat_d = 0;
212 } else if (!strcmp(ptr, "xml")) {
213 informat_s = 0;
214 informat_d = LYD_XML;
215 } else if (!strcmp(ptr, "json")) {
216 informat_s = 0;
217 informat_d = LYD_JSON;
218 } else {
219 fprintf(stderr, "yanglint error: input file in an unknown format \"%s\".\n", ptr);
220 return 0;
221 }
222 } else {
223 fprintf(stderr, "yanglint error: input file \"%s\" without file extension - unknown format.\n", filename);
224 return 0;
225 }
226
227 if (data) {
228 (*data) = informat_d;
229 }
230 if (schema) {
231 (*schema) = informat_s;
232 }
233
234 if (informat_s) {
235 return 1;
236 } else {
237 return 2;
238 }
239}
240
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200241int
242main_ni(int argc, char* argv[])
243{
244 int ret = EXIT_FAILURE;
Michal Vasko568b1952018-01-30 15:53:30 +0100245 int opt, opt_index = 0, i, featsize = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200246 struct option options[] = {
Radek Krejci922ba5a2017-08-03 11:39:00 +0200247 {"auto", no_argument, NULL, 'a'},
Radek Krejcifeb903c2016-10-27 17:29:07 +0200248 {"default", required_argument, NULL, 'd'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200249 {"format", required_argument, NULL, 'f'},
250 {"features", required_argument, NULL, 'F'},
Radek Krejcif9c28262017-01-25 11:36:02 +0100251 {"tree-print-groupings", no_argument, NULL, 'g'},
Michal Vasko568b1952018-01-30 15:53:30 +0100252 {"tree-print-uses", no_argument, NULL, 'u'},
253 {"tree-no-leafref-target", no_argument, NULL, 'n'},
254 {"tree-path", required_argument, NULL, 'P'},
255 {"tree-line-length", required_argument, NULL, 'L'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200256 {"help", no_argument, NULL, 'h'},
257 {"tree-help", no_argument, NULL, 'H'},
Radek Krejci819dd4b2017-03-07 15:35:48 +0100258 {"allimplemented", no_argument, NULL, 'i'},
Radek Krejcid57525c2017-09-05 16:37:14 +0200259 {"list", no_argument, NULL, 'l'},
Radek Krejci7fa15692017-08-08 10:16:00 +0200260 {"merge", no_argument, NULL, 'm'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200261 {"output", required_argument, NULL, 'o'},
262 {"path", required_argument, NULL, 'p'},
Radek Krejci27105402017-08-03 15:43:12 +0200263 {"running", required_argument, NULL, 'r'},
Radek Krejci65b372b2017-03-10 11:00:10 +0100264 {"strict", no_argument, NULL, 's'},
Michal Vasko568b1952018-01-30 15:53:30 +0100265 {"type", required_argument, NULL, 't'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200266 {"version", no_argument, NULL, 'v'},
267 {"verbose", no_argument, NULL, 'V'},
Michal Vasko5e5172b2017-04-04 11:00:40 +0200268#ifndef NDEBUG
Michal Vasko1c8edc72017-04-03 13:49:51 +0200269 {"debug", required_argument, NULL, 'G'},
Michal Vasko5e5172b2017-04-04 11:00:40 +0200270#endif
Radek Krejci1770f612017-03-17 16:34:07 +0100271 {NULL, required_argument, NULL, 'y'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200272 {NULL, 0, NULL, 0}
273 };
274 FILE *out = stdout;
275 struct ly_ctx *ctx = NULL;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200276 const struct lys_module *mod;
277 LYS_OUTFORMAT outformat_s = 0;
278 LYS_INFORMAT informat_s;
Radek Krejci1770f612017-03-17 16:34:07 +0100279 LYD_FORMAT informat_d, outformat_d = 0, ylformat = 0;
Radek Krejci618405d2017-03-25 19:42:03 -0500280 struct ly_set *searchpaths = NULL;
Michal Vasko568b1952018-01-30 15:53:30 +0100281 const char *running_file = NULL, *envelope_s = NULL, *outtarget_s = NULL;
Radek Krejcie02fb702017-06-13 16:53:22 +0200282 char **feat = NULL, *ptr, *featlist, *ylpath = NULL, *dir;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200283 struct stat st;
284 uint32_t u;
Radek Krejcid57525c2017-09-05 16:37:14 +0200285 int options_dflt = 0, options_parser = 0, options_ctx = 0, envelope = 0, autodetection = 0, merge = 0, list = 0;
Michal Vasko568b1952018-01-30 15:53:30 +0100286 int outoptions_s = 0, outline_length_s = 0;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200287 struct dataitem {
288 const char *filename;
Radek Krejci27105402017-08-03 15:43:12 +0200289 struct lyxml_elem *xml;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200290 struct lyd_node *tree;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200291 struct dataitem *next;
Radek Krejci27105402017-08-03 15:43:12 +0200292 LYD_FORMAT format;
293 int type;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200294 } *data = NULL, *data_item, *data_prev = NULL;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200295 struct ly_set *mods = NULL;
Radek Krejci7fa15692017-08-08 10:16:00 +0200296 struct lyd_node *running = NULL, *subroot, *next, *node;
Radek Krejci6de1ada2017-02-27 11:26:43 +0100297 void *p;
Radek Krejcie02fb702017-06-13 16:53:22 +0200298 int index = 0;
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200299 struct lyxml_elem *iter, *elem;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200300
301 opterr = 0;
Michal Vasko5e5172b2017-04-04 11:00:40 +0200302#ifndef NDEBUG
Michal Vasko568b1952018-01-30 15:53:30 +0100303 while ((opt = getopt_long(argc, argv, "ad:f:F:gunP:L:hHilmo:p:r:st:vVG:y:", options, &opt_index)) != -1)
Michal Vasko5e5172b2017-04-04 11:00:40 +0200304#else
Michal Vasko568b1952018-01-30 15:53:30 +0100305 while ((opt = getopt_long(argc, argv, "ad:f:F:gunP:L:hHilmo:p:r:st:vVy:", options, &opt_index)) != -1)
Michal Vasko5e5172b2017-04-04 11:00:40 +0200306#endif
307 {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200308 switch (opt) {
Radek Krejci922ba5a2017-08-03 11:39:00 +0200309 case 'a':
310 envelope = 1;
311 break;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200312 case 'd':
313 if (!strcmp(optarg, "all")) {
314 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL;
315 } else if (!strcmp(optarg, "all-tagged")) {
316 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
317 } else if (!strcmp(optarg, "trim")) {
318 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_TRIM;
319 } else if (!strcmp(optarg, "implicit-tagged")) {
320 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
321 } else {
322 fprintf(stderr, "yanglint error: unknown default mode %s\n", optarg);
323 help(1);
324 goto cleanup;
325 }
326 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200327 case 'f':
328 if (!strcasecmp(optarg, "tree")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200329 outformat_s = LYS_OUT_TREE;
330 outformat_d = 0;
Michal Vasko568b1952018-01-30 15:53:30 +0100331 } else if (!strcasecmp(optarg, "tree-rfc")) {
332 outformat_s = LYS_OUT_TREE;
333 outoptions_s |= LYS_OUTOPT_TREE_RFC;
334 outformat_d = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200335 } else if (!strcasecmp(optarg, "yin")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200336 outformat_s = LYS_OUT_YIN;
337 outformat_d = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200338 } else if (!strcasecmp(optarg, "yang")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200339 outformat_s = LYS_OUT_YANG;
340 outformat_d = 0;
341 } else if (!strcasecmp(optarg, "xml")) {
342 outformat_s = 0;
343 outformat_d = LYD_XML;
344 } else if (!strcasecmp(optarg, "json")) {
345 outformat_s = 0;
346 outformat_d = LYD_JSON;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200347 } else {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200348 fprintf(stderr, "yanglint error: unknown output format %s\n", optarg);
349 help(1);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200350 goto cleanup;
351 }
352 break;
353 case 'F':
354 featsize++;
355 if (!feat) {
Radek Krejci6de1ada2017-02-27 11:26:43 +0100356 p = malloc(sizeof *feat);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200357 } else {
Radek Krejci6de1ada2017-02-27 11:26:43 +0100358 p = realloc(feat, featsize * sizeof *feat);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200359 }
Radek Krejci6de1ada2017-02-27 11:26:43 +0100360 if (!p) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200361 fprintf(stderr, "yanglint error: Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
Radek Krejci6de1ada2017-02-27 11:26:43 +0100362 goto cleanup;
363 }
364 feat = p;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200365 feat[featsize - 1] = strdup(optarg);
366 ptr = strchr(feat[featsize - 1], ':');
367 if (!ptr) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200368 fprintf(stderr, "yanglint error: Invalid format of the features specification (%s)", optarg);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200369 goto cleanup;
370 }
371 *ptr = '\0';
372
373 break;
Radek Krejcif9c28262017-01-25 11:36:02 +0100374 case 'g':
Michal Vasko568b1952018-01-30 15:53:30 +0100375 outoptions_s |= LYS_OUTOPT_TREE_GROUPING;
376 break;
377 case 'u':
378 outoptions_s |= LYS_OUTOPT_TREE_USES;
379 break;
380 case 'n':
381 outoptions_s |= LYS_OUTOPT_TREE_NO_LEAFREF;
382 break;
383 case 'P':
384 outtarget_s = optarg;
385 break;
386 case 'L':
387 outline_length_s = atoi(optarg);
Radek Krejcif9c28262017-01-25 11:36:02 +0100388 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200389 case 'h':
390 help(0);
391 ret = EXIT_SUCCESS;
392 goto cleanup;
393 case 'H':
394 tree_help();
395 ret = EXIT_SUCCESS;
396 goto cleanup;
Radek Krejci819dd4b2017-03-07 15:35:48 +0100397 case 'i':
Radek Krejcidd3263a2017-07-15 11:50:09 +0200398 options_ctx |= LY_CTX_ALLIMPLEMENTED;
Radek Krejci819dd4b2017-03-07 15:35:48 +0100399 break;
Radek Krejci54c05192017-07-18 17:46:07 +0200400 case 'l':
Radek Krejci7055a772017-08-08 10:45:30 +0200401 list = 1;
402 break;
Radek Krejci7fa15692017-08-08 10:16:00 +0200403 case 'm':
404 merge = 1;
Radek Krejci54c05192017-07-18 17:46:07 +0200405 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200406 case 'o':
Radek Krejcid9732442016-10-20 13:29:34 +0200407 if (out != stdout) {
408 fclose(out);
409 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200410 out = fopen(optarg, "w");
411 if (!out) {
412 fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
413 goto cleanup;
414 }
415 break;
416 case 'p':
Radek Krejci618405d2017-03-25 19:42:03 -0500417 if (stat(optarg, &st) == -1) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200418 fprintf(stderr, "yanglint error: Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200419 goto cleanup;
420 }
421 if (!S_ISDIR(st.st_mode)) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200422 fprintf(stderr, "yanglint error: Provided search path is not a directory.\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200423 goto cleanup;
424 }
Radek Krejci618405d2017-03-25 19:42:03 -0500425 if (!searchpaths) {
426 searchpaths = ly_set_new();
427 }
428 ly_set_add(searchpaths, optarg, 0);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200429 break;
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200430 case 'r':
Radek Krejcia1ec4d12017-08-03 14:01:06 +0200431 if (running_file || (options_parser & LYD_OPT_NOEXTDEPS)) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200432 fprintf(stderr, "yanglint error: The running datastore (-r) cannot be set multiple times.\n");
433 goto cleanup;
434 }
Radek Krejci27105402017-08-03 15:43:12 +0200435 if (optarg[0] == '!') {
Radek Krejcia1ec4d12017-08-03 14:01:06 +0200436 /* ignore extenral dependencies to the running datastore */
437 options_parser |= LYD_OPT_NOEXTDEPS;
Radek Krejci27105402017-08-03 15:43:12 +0200438 } else {
439 /* external file with the running datastore */
440 running_file = optarg;
Radek Krejcia1ec4d12017-08-03 14:01:06 +0200441 }
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200442 break;
Radek Krejci65b372b2017-03-10 11:00:10 +0100443 case 's':
444 options_parser |= LYD_OPT_STRICT;
445 break;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200446 case 't':
447 if (!strcmp(optarg, "auto")) {
Radek Krejci27105402017-08-03 15:43:12 +0200448 options_parser = (options_parser & ~LYD_OPT_TYPEMASK);
449 autodetection = 1;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200450 } else if (!strcmp(optarg, "config")) {
451 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
452 } else if (!strcmp(optarg, "get")) {
453 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
454 } else if (!strcmp(optarg, "getconfig")) {
455 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
456 } else if (!strcmp(optarg, "edit")) {
457 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
458 } else if (!strcmp(optarg, "data")) {
Radek Krejci06f8bb92017-08-02 15:36:25 +0200459 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
Radek Krejci2d296ce2017-08-02 10:40:39 +0200460 } else if (!strcmp(optarg, "rpc")) {
461 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200462 } else if (!strcmp(optarg, "rpcreply")) {
463 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
Radek Krejci2d296ce2017-08-02 10:40:39 +0200464 } else if (!strcmp(optarg, "notif")) {
465 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200466 } else {
467 fprintf(stderr, "yanglint error: unknown data tree type %s\n", optarg);
468 help(1);
469 goto cleanup;
470 }
471 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200472 case 'v':
473 version();
474 ret = EXIT_SUCCESS;
475 goto cleanup;
476 case 'V':
Radek Krejcid80c8602016-10-25 11:56:03 +0200477 verbose++;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200478 break;
Michal Vasko5e5172b2017-04-04 11:00:40 +0200479#ifndef NDEBUG
Michal Vasko1c8edc72017-04-03 13:49:51 +0200480 case 'G':
481 u = 0;
482 ptr = optarg;
483 while (ptr[0]) {
484 if (!strncmp(ptr, "dict", 4)) {
485 u |= LY_LDGDICT;
486 ptr += 4;
487 } else if (!strncmp(ptr, "yang", 4)) {
488 u |= LY_LDGYANG;
489 ptr += 4;
490 } else if (!strncmp(ptr, "yin", 3)) {
491 u |= LY_LDGYIN;
492 ptr += 3;
493 } else if (!strncmp(ptr, "xpath", 5)) {
494 u |= LY_LDGXPATH;
495 ptr += 5;
496 } else if (!strncmp(ptr, "diff", 4)) {
497 u |= LY_LDGDIFF;
498 ptr += 4;
499 }
500
501 if (ptr[0]) {
502 if (ptr[0] != ',') {
503 fprintf(stderr, "yanglint error: unknown debug group string \"%s\"\n", optarg);
504 goto cleanup;
505 }
506 ++ptr;
507 }
508 }
509 ly_verb_dbg(u);
510 break;
Michal Vasko5e5172b2017-04-04 11:00:40 +0200511#endif
Radek Krejci1770f612017-03-17 16:34:07 +0100512 case 'y':
513 ptr = strrchr(optarg, '.');
514 if (ptr) {
515 ptr++;
516 if (!strcmp(ptr, "xml")) {
517 ylformat = LYD_XML;
518 } else if (!strcmp(ptr, "json")) {
519 ylformat = LYD_JSON;
520 } else {
521 fprintf(stderr, "yanglint error: yang-library file in an unknown format \"%s\".\n", ptr);
522 goto cleanup;
523 }
524 } else {
525 fprintf(stderr, "yanglint error: yang-library file in an unknown format.\n");
526 goto cleanup;
527 }
528 ylpath = optarg;
529 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200530 default:
531 help(1);
532 if (optopt) {
533 fprintf(stderr, "yanglint error: invalid option: -%c\n", optopt);
534 } else {
535 fprintf(stderr, "yanglint error: invalid option: %s\n", argv[optind - 1]);
536 }
537 goto cleanup;
538 }
539 }
540
Radek Krejcifeb903c2016-10-27 17:29:07 +0200541 /* check options compatibility */
Radek Krejci7055a772017-08-08 10:45:30 +0200542 if (!list && optind >= argc) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200543 help(1);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200544 fprintf(stderr, "yanglint error: missing <file> to process\n");
Radek Krejcid57525c2017-09-05 16:37:14 +0200545 goto cleanup;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200546 }
547 if (outformat_s && outformat_s != LYS_OUT_TREE && (optind + 1) < argc) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200548 /* we have multiple schemas to be printed as YIN or YANG */
Radek Krejcifeb903c2016-10-27 17:29:07 +0200549 fprintf(stderr, "yanglint error: too many schemas to convert and store.\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200550 goto cleanup;
551 }
Michal Vasko568b1952018-01-30 15:53:30 +0100552 if (outoptions_s || outtarget_s || outline_length_s) {
Radek Krejcif9c28262017-01-25 11:36:02 +0100553 if (outformat_d || (outformat_s && outformat_s != LYS_OUT_TREE)) {
554 /* we have --tree-print-grouping with other output format than tree */
555 fprintf(stderr,
Michal Vasko568b1952018-01-30 15:53:30 +0100556 "yanglint warning: --tree options take effect only in case of the tree output format.\n");
Radek Krejcif9c28262017-01-25 11:36:02 +0100557 }
558 }
Radek Krejci7fa15692017-08-08 10:16:00 +0200559 if (merge) {
560 if (autodetection || (options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF))) {
561 fprintf(stderr, "yanglint warning: merging not allowed, ignoring option -m.\n");
562 merge = 0;
563 } else {
564 /* first, files will be parsed as trusted to allow missing data, then the data trees will be merged
565 * and the result will be validated */
566 options_parser |= LYD_OPT_TRUSTED;
567 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200568 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200569 if (!outformat_d && options_dflt) {
Radek Krejcibf026072017-08-03 09:23:57 +0200570 /* we have options for printing default nodes, but data output not specified */
571 fprintf(stderr, "yanglint warning: default mode is ignored when not printing data.\n");
Radek Krejcifeb903c2016-10-27 17:29:07 +0200572 }
Radek Krejci27105402017-08-03 15:43:12 +0200573 if (outformat_s && (options_parser || autodetection)) {
Radek Krejci65b372b2017-03-10 11:00:10 +0100574 /* we have options for printing data tree, but output is schema */
Radek Krejcibf026072017-08-03 09:23:57 +0200575 fprintf(stderr, "yanglint warning: data parser options are ignored when printing schema.\n");
Radek Krejcifeb903c2016-10-27 17:29:07 +0200576 }
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200577 if (running_file && (!autodetection && !(options_parser & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF)))) {
Radek Krejci27105402017-08-03 15:43:12 +0200578 fprintf(stderr, "yanglint warning: running datastore applies only to RPCs or Notifications.\n");
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200579 /* ignore running datastore file */
580 running_file = NULL;
581 }
Radek Krejci06f8bb92017-08-02 15:36:25 +0200582 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_DATA) {
583 /* add option to ignore ietf-yang-library data for implicit data type */
584 options_parser |= LYD_OPT_DATA_NO_YANGLIB;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200585 }
586
587 /* set callback for printing libyang messages */
588 ly_set_log_clb(libyang_verbclb, 1);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200589
590 /* create libyang context */
Radek Krejci1770f612017-03-17 16:34:07 +0100591 if (ylpath) {
Radek Krejcidd3263a2017-07-15 11:50:09 +0200592 ctx = ly_ctx_new_ylpath(searchpaths ? (const char*)searchpaths->set.g[0] : NULL, ylpath, ylformat, options_ctx);
Radek Krejci1770f612017-03-17 16:34:07 +0100593 } else {
Radek Krejcidd3263a2017-07-15 11:50:09 +0200594 ctx = ly_ctx_new(NULL, options_ctx);
Radek Krejci1770f612017-03-17 16:34:07 +0100595 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200596 if (!ctx) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200597 goto cleanup;
598 }
599
Radek Krejci618405d2017-03-25 19:42:03 -0500600 /* set searchpaths */
601 if (searchpaths) {
602 for (u = 0; u < searchpaths->number; u++) {
603 ly_ctx_set_searchdir(ctx, (const char*)searchpaths->set.g[u]);
604 }
Radek Krejcie02fb702017-06-13 16:53:22 +0200605 index = u + 1;
Radek Krejci618405d2017-03-25 19:42:03 -0500606 }
607
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200608 /* derefered setting of verbosity in libyang after context initiation */
Radek Krejciff5e33f2016-10-25 14:24:26 +0200609 ly_verb(verbose);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200610
Radek Krejcifeb903c2016-10-27 17:29:07 +0200611 mods = ly_set_new();
612
Radek Krejcie02fb702017-06-13 16:53:22 +0200613
Radek Krejcifeb903c2016-10-27 17:29:07 +0200614 /* divide input files */
615 for (i = 0; i < argc - optind; i++) {
616 /* get the file format */
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200617 if (!get_fileformat(argv[optind + i], &informat_s, &informat_d)) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200618 goto cleanup;
619 }
620
621 if (informat_s) {
622 /* load/validate schema */
623 if (verbose >= 2) {
624 fprintf(stdout, "Validating %s schema file.\n", argv[optind + i]);
625 }
Radek Krejcie02fb702017-06-13 16:53:22 +0200626 dir = strdup(argv[optind + i]);
627 ly_ctx_set_searchdir(ctx, dirname(dir));
Radek Krejcifeb903c2016-10-27 17:29:07 +0200628 mod = lys_parse_path(ctx, argv[optind + i], informat_s);
Radek Krejcie02fb702017-06-13 16:53:22 +0200629 ly_ctx_unset_searchdirs(ctx, index);
630 free(dir);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200631 if (!mod) {
632 goto cleanup;
633 }
634 ly_set_add(mods, (void *)mod, 0);
635 } else {
Radek Krejci27105402017-08-03 15:43:12 +0200636 if (autodetection && informat_d != LYD_XML) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200637 /* data file content autodetection is possible only for XML input */
638 fprintf(stderr, "yanglint error: data type autodetection is applicable only to XML files.\n");
639 goto cleanup;
640 }
641
642 /* remember data filename and its format */
643 if (!data) {
644 data = data_item = malloc(sizeof *data);
645 } else {
646 for (data_item = data; data_item->next; data_item = data_item->next);
647 data_item->next = malloc(sizeof *data_item);
648 data_item = data_item->next;
649 }
650 data_item->filename = argv[optind + i];
651 data_item->format = informat_d;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200652 data_item->type = options_parser & LYD_OPT_TYPEMASK;
653 data_item->tree = NULL;
Radek Krejci27105402017-08-03 15:43:12 +0200654 data_item->xml = NULL;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200655 data_item->next = NULL;
656 }
657 }
658
Radek Krejci7055a772017-08-08 10:45:30 +0200659 if (outformat_d && !data && !list) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200660 fprintf(stderr, "yanglint error: no input data file for the specified data output format.\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200661 goto cleanup;
662 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200663
664 /* enable specified features, if not specified, all the module's features are enabled */
665 u = 4; /* skip internal libyang modules */
666 while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
667 for (i = 0; i < featsize; i++) {
668 if (!strcmp(feat[i], mod->name)) {
669 /* parse features spec */
670 featlist = strdup(feat[i] + strlen(feat[i]) + 1);
671 ptr = NULL;
672 while((ptr = strtok(ptr ? NULL : featlist, ","))) {
673 if (verbose >= 2) {
674 fprintf(stdout, "Enabling feature %s in module %s.\n", ptr, mod->name);
675 }
676 if (lys_features_enable(mod, ptr)) {
677 fprintf(stderr, "Feature %s not defined in module %s.\n", ptr, mod->name);
678 }
679 }
680 free(featlist);
681 break;
682 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200683 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200684 if (i == featsize) {
685 if (verbose >= 2) {
686 fprintf(stdout, "Enabling all features in module %s.\n", mod->name);
687 }
688 lys_features_enable(mod, "*");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200689 }
690 }
691
Radek Krejcifeb903c2016-10-27 17:29:07 +0200692 /* convert (print) to FORMAT */
693 if (outformat_s) {
694 for (u = 0; u < mods->number; u++) {
Michal Vasko568b1952018-01-30 15:53:30 +0100695 lys_print_file(out, (struct lys_module *)mods->set.g[u], outformat_s, outtarget_s, outline_length_s, outoptions_s);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200696 }
Radek Krejcie14d2eb2016-11-04 17:35:47 +0100697 } else if (data) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200698 ly_errno = 0;
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200699
700 /* prepare running datastore when specified for RPC/Notification */
701 if (running_file) {
702 /* get the file format */
703 if (!get_fileformat(running_file, NULL, &informat_d)) {
704 goto cleanup;
705 } else if (!informat_d) {
706 fprintf(stderr, "yanglint error: The running data are expected in XML or JSON format.\n");
707 goto cleanup;
708 }
709 running = lyd_parse_path(ctx, running_file, informat_d, LYD_OPT_DATA_NO_YANGLIB);
710 if (!running) {
711 fprintf(stderr, "yanglint error: Failed to parse the running datastore file for RPC/Notification validation.\n");
712 goto cleanup;
713 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200714 }
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200715
Radek Krejci922ba5a2017-08-03 11:39:00 +0200716 for (data_item = data, data_prev = NULL; data_item; data_prev = data_item, data_item = data_item->next) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200717 /* parse data file - via LYD_OPT_TRUSTED postpone validation when all data are loaded and merged */
Radek Krejci27105402017-08-03 15:43:12 +0200718 if (autodetection) {
719 /* erase option not covered by LYD_OPT_TYPEMASK, but used according to the type */
720 options_parser &= ~LYD_OPT_DATA_NO_YANGLIB;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200721 /* automatically detect data type from the data top level */
Radek Krejci27105402017-08-03 15:43:12 +0200722 data_item->xml = lyxml_parse_path(ctx, data_item->filename, 0);
723 if (!data_item->xml) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200724 fprintf(stderr, "yanglint error: parsing XML data for data type autodetection failed.\n");
725 goto cleanup;
726 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200727
Radek Krejcifeb903c2016-10-27 17:29:07 +0200728 /* NOTE: namespace is ignored to simplify usage of this feature */
Radek Krejci27105402017-08-03 15:43:12 +0200729 if (!strcmp(data_item->xml->name, "data")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200730 if (verbose >= 2) {
731 fprintf(stdout, "Parsing %s as complete datastore.\n", data_item->filename);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200732 }
Radek Krejci06f8bb92017-08-02 15:36:25 +0200733 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200734 data_item->type = LYD_OPT_DATA;
Radek Krejci27105402017-08-03 15:43:12 +0200735 } else if (!strcmp(data_item->xml->name, "config")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200736 if (verbose >= 2) {
737 fprintf(stdout, "Parsing %s as config data.\n", data_item->filename);
738 }
739 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200740 data_item->type = LYD_OPT_CONFIG;
Radek Krejci27105402017-08-03 15:43:12 +0200741 } else if (!strcmp(data_item->xml->name, "get-reply")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200742 if (verbose >= 2) {
743 fprintf(stdout, "Parsing %s as <get> reply data.\n", data_item->filename);
744 }
745 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200746 data_item->type = LYD_OPT_GET;
Radek Krejci27105402017-08-03 15:43:12 +0200747 } else if (!strcmp(data_item->xml->name, "get-config-reply")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200748 if (verbose >= 2) {
749 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", data_item->filename);
750 }
751 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200752 data_item->type = LYD_OPT_GETCONFIG;
Radek Krejci27105402017-08-03 15:43:12 +0200753 } else if (!strcmp(data_item->xml->name, "edit-config")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200754 if (verbose >= 2) {
755 fprintf(stdout, "Parsing %s as <edit-config> data.\n", data_item->filename);
756 }
757 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200758 data_item->type = LYD_OPT_EDIT;
Radek Krejci27105402017-08-03 15:43:12 +0200759 } else if (!strcmp(data_item->xml->name, "rpc")) {
Radek Krejci2d296ce2017-08-02 10:40:39 +0200760 if (verbose >= 2) {
761 fprintf(stdout, "Parsing %s as <rpc> data.\n", data_item->filename);
762 }
763 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200764 data_item->type = LYD_OPT_RPC;
Radek Krejci27105402017-08-03 15:43:12 +0200765 } else if (!strcmp(data_item->xml->name, "rpc-reply")) {
Radek Krejci922ba5a2017-08-03 11:39:00 +0200766 if (verbose >= 2) {
767 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", data_item->filename);
768 }
Radek Krejci27105402017-08-03 15:43:12 +0200769
770 data_item->type = LYD_OPT_RPCREPLY;
771 if (!data_item->next || (data_prev && !data_prev->tree)) {
772 fprintf(stderr, "RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
Radek Krejci922ba5a2017-08-03 11:39:00 +0200773 goto cleanup;
774 }
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200775
Radek Krejci27105402017-08-03 15:43:12 +0200776 continue;
777 } else if (!strcmp(data_item->xml->name, "notification")) {
Radek Krejci2d296ce2017-08-02 10:40:39 +0200778 if (verbose >= 2) {
779 fprintf(stdout, "Parsing %s as <notification> data.\n", data_item->filename);
780 }
781 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200782 data_item->type = LYD_OPT_NOTIF;
Radek Krejci9604a662017-08-03 13:31:50 +0200783
784 /* ignore eventTime element if present */
Radek Krejci27105402017-08-03 15:43:12 +0200785 while (data_item->xml->child && !strcmp(data_item->xml->child->name, "eventTime")) {
786 lyxml_free(ctx, data_item->xml->child);
Radek Krejci9604a662017-08-03 13:31:50 +0200787 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200788 } else {
789 fprintf(stderr, "yanglint error: invalid top-level element \"%s\" for data type autodetection.\n",
Radek Krejci27105402017-08-03 15:43:12 +0200790 data_item->xml->name);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200791 goto cleanup;
792 }
793
Radek Krejci27105402017-08-03 15:43:12 +0200794 data_item->tree = lyd_parse_xml(ctx, &data_item->xml->child, options_parser, running);
795 if (data_prev && data_prev->type == LYD_OPT_RPCREPLY) {
796parse_reply:
797 /* check result of the RPC parsing, we are going to do another parsing in this step */
798 if (ly_errno) {
799 goto cleanup;
800 }
801
802 /* check that we really have RPC for the reply */
803 if (data_item->type != LYD_OPT_RPC) {
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200804 fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_prev->filename);
Radek Krejci27105402017-08-03 15:43:12 +0200805 goto cleanup;
806 }
807
Radek Krejci8a706172017-10-27 15:38:28 +0200808 if (data_prev->format == LYD_XML) {
809 /* ignore <ok> and <rpc-error> elements if present */
810 u = 0;
811 LY_TREE_FOR_SAFE(data_prev->xml->child, iter, elem) {
812 if (!strcmp(data_prev->xml->child->name, "ok")) {
813 if (u) {
814 /* rpc-error or ok already present */
815 u = 0x8; /* error flag */
816 } else {
817 u = 0x1 | 0x4; /* <ok> flag with lyxml_free() flag */
818 }
819 } else if (!strcmp(data_prev->xml->child->name, "rpc-error")) {
820 if (u && (u & 0x1)) {
821 /* ok already present, rpc-error can be present multiple times */
822 u = 0x8; /* error flag */
823 } else {
824 u = 0x2 | 0x4; /* <rpc-error> flag with lyxml_free() flag */
825 }
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200826 }
Radek Krejci8a706172017-10-27 15:38:28 +0200827
828 if (u == 0x8) {
829 fprintf(stderr, "yanglint error: Invalid RPC reply (%s) content.\n", data_prev->filename);
830 goto cleanup;
831 } else if (u & 0x4) {
832 lyxml_free(ctx, data_prev->xml->child);
833 u &= ~0x4; /* unset lyxml_free() flag */
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200834 }
835 }
836
Radek Krejci8a706172017-10-27 15:38:28 +0200837 /* finally, parse RPC reply from the previous step */
838 data_prev->tree = lyd_parse_xml(ctx, &data_prev->xml->child,
839 (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, running);
840 } else { /* LYD_JSON */
841 data_prev->tree = lyd_parse_path(ctx, data_prev->filename, data_item->format,
842 (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY, data_item->tree, running);
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200843 }
Radek Krejci922ba5a2017-08-03 11:39:00 +0200844 }
Radek Krejci27105402017-08-03 15:43:12 +0200845 } else if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_RPCREPLY) {
846 if (data_prev && !data_prev->tree) {
847 /* now we should have RPC for the preceding RPC reply */
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200848 data_item->tree = lyd_parse_path(ctx, data_item->filename, data_item->format,
849 (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC, running);
850 data_item->type = LYD_OPT_RPC;
Radek Krejci27105402017-08-03 15:43:12 +0200851 goto parse_reply;
852 } else {
853 /* now we have RPC reply which will be parsed in next step together with its RPC */
854 if (!data_item->next) {
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200855 fprintf(stderr, "yanglint error: RPC reply (%s) must be paired with the original RPC, see help.\n", data_item->filename);
Radek Krejci27105402017-08-03 15:43:12 +0200856 goto cleanup;
857 }
Radek Krejci8a706172017-10-27 15:38:28 +0200858 if (data_item->format == LYD_XML) {
859 /* create rpc-reply container to unify handling with autodetection */
860 data_item->xml = calloc(1, sizeof *data_item->xml);
861 if (!data_item->xml) {
862 fprintf(stderr, "yanglint error: Memory allocation failed failed.\n");
863 goto cleanup;
864 }
865 data_item->xml->name = lydict_insert(ctx, "rpc-reply", 9);
866 data_item->xml->prev = data_item->xml;
867 data_item->xml->child = lyxml_parse_path(ctx, data_item->filename, LYXML_PARSE_MULTIROOT | LYXML_PARSE_NOMIXEDCONTENT);
868 if (data_item->xml->child) {
869 data_item->xml->child->parent = data_item->xml;
870 }
Radek Krejcid76cb5d2017-08-08 13:57:11 +0200871 }
Radek Krejci27105402017-08-03 15:43:12 +0200872 continue;
873 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200874 } else {
Radek Krejci922ba5a2017-08-03 11:39:00 +0200875 data_item->tree = lyd_parse_path(ctx, data_item->filename, data_item->format, options_parser, running);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200876 }
877 if (ly_errno) {
878 goto cleanup;
879 }
Radek Krejci7fa15692017-08-08 10:16:00 +0200880
881 if (merge && data != data_item) {
882 if (!data->tree) {
883 data->tree = data_item->tree;
884 } else if (data_item->tree) {
885 /* merge results */
886 if (lyd_merge(data->tree, data_item->tree, LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
887 fprintf(stderr, "yanglint error: merging multiple data trees failed.\n");
888 goto cleanup;
889 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200890 }
Radek Krejci7fa15692017-08-08 10:16:00 +0200891 data_item->tree = NULL;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200892 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200893 }
Radek Krejci7fa15692017-08-08 10:16:00 +0200894
895 if (merge) {
896 /* validate the merged data tree, do not trust the input, invalidate all the data first */
897 LY_TREE_FOR(data->tree, subroot) {
Radek Krejci02f5d952016-10-31 13:09:57 +0100898 LY_TREE_DFS_BEGIN(subroot, next, node) {
Michal Vaskoe3886bb2017-01-02 11:33:28 +0100899 node->validity = LYD_VAL_OK;
900 switch (node->schema->nodetype) {
901 case LYS_LEAFLIST:
902 case LYS_LEAF:
903 if (((struct lys_node_leaf *)node->schema)->type.base == LY_TYPE_LEAFREF) {
904 node->validity |= LYD_VAL_LEAFREF;
905 }
906 break;
907 case LYS_LIST:
908 node->validity |= LYD_VAL_UNIQUE;
Radek Krejci7fa15692017-08-08 10:16:00 +0200909 /* falls through */
Michal Vaskoe3886bb2017-01-02 11:33:28 +0100910 case LYS_CONTAINER:
911 case LYS_NOTIF:
912 case LYS_RPC:
913 case LYS_ACTION:
914 node->validity |= LYD_VAL_MAND;
915 break;
916 default:
917 break;
918 }
Radek Krejci02f5d952016-10-31 13:09:57 +0100919 LY_TREE_DFS_END(subroot, next, node)
920 }
921 }
Radek Krejci7fa15692017-08-08 10:16:00 +0200922 if (lyd_validate(&data->tree, options_parser & ~LYD_OPT_TRUSTED, ctx)) {
Radek Krejci7281a762016-10-31 09:35:04 +0100923 goto cleanup;
924 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200925 }
926
Radek Krejcifeb903c2016-10-27 17:29:07 +0200927 /* print only if data output format specified */
Radek Krejci922ba5a2017-08-03 11:39:00 +0200928 if (outformat_d) {
929 for (data_item = data; data_item; data_item = data_item->next) {
Radek Krejci7fa15692017-08-08 10:16:00 +0200930 if (!merge && verbose >= 2) {
Radek Krejci922ba5a2017-08-03 11:39:00 +0200931 fprintf(stdout, "File %s:\n", data_item->filename);
932 }
933 if (outformat_d == LYD_XML && envelope) {
934 switch (data_item->type) {
935 case LYD_OPT_DATA:
936 envelope_s = "data";
937 break;
938 case LYD_OPT_CONFIG:
939 envelope_s = "config";
940 break;
941 case LYD_OPT_GET:
942 envelope_s = "get-reply";
943 break;
944 case LYD_OPT_GETCONFIG:
945 envelope_s = "get-config-reply";
946 break;
947 case LYD_OPT_EDIT:
948 envelope_s = "edit-config";
949 break;
950 case LYD_OPT_RPC:
951 envelope_s = "rpc";
952 break;
953 case LYD_OPT_RPCREPLY:
954 envelope_s = "rpc-reply";
955 break;
956 case LYD_OPT_NOTIF:
957 envelope_s = "notification";
958 break;
959 }
960 fprintf(out, "<%s>\n", envelope_s);
961 if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
962 /* action */
963 fprintf(out, "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">\n");
964 }
965 }
966 lyd_print_file(out, (data_item->type == LYD_OPT_RPCREPLY) ? data_item->tree->child : data_item->tree,
967 outformat_d, LYP_WITHSIBLINGS | LYP_FORMAT | options_dflt);
968 if (envelope_s) {
969 if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
970 fprintf(out, "</action>\n");
971 }
972 fprintf(out, "</%s>\n", envelope_s);
973 }
Radek Krejci7fa15692017-08-08 10:16:00 +0200974 if (merge) {
975 /* stop after first item */
976 break;
977 }
Radek Krejci922ba5a2017-08-03 11:39:00 +0200978 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200979 }
980 }
Radek Krejci54c05192017-07-18 17:46:07 +0200981
Radek Krejci7055a772017-08-08 10:45:30 +0200982 if (list) {
983 print_list(out, ctx, outformat_d);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200984 }
985
986 ret = EXIT_SUCCESS;
987
988cleanup:
Radek Krejci4fd7ef22016-10-20 13:26:41 +0200989 if (out && out != stdout) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200990 fclose(out);
991 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200992 ly_set_free(mods);
Radek Krejci618405d2017-03-25 19:42:03 -0500993 ly_set_free(searchpaths);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200994 for (i = 0; i < featsize; i++) {
995 free(feat[i]);
996 }
997 free(feat);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200998 for (; data; data = data_item) {
999 data_item = data->next;
Radek Krejci27105402017-08-03 15:43:12 +02001000 lyxml_free(ctx, data->xml);
Radek Krejci922ba5a2017-08-03 11:39:00 +02001001 lyd_free_withsiblings(data->tree);
Radek Krejcifeb903c2016-10-27 17:29:07 +02001002 free(data);
1003 }
Radek Krejcid76cb5d2017-08-08 13:57:11 +02001004 lyd_free_withsiblings(running);
Radek Krejci6ef5efc2016-10-10 16:38:49 +02001005 ly_ctx_destroy(ctx, NULL);
1006
1007 return ret;
1008}