blob: b49a53ddc9805e1a5fea9f47c5611566b752e840 [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"
28#include "../../src/libyang.h"
29
30volatile int verbose = 0;
31
32void
33help(int shortout)
34{
Radek Krejcifeb903c2016-10-27 17:29:07 +020035 fprintf(stdout, "Usage:\n");
36 fprintf(stdout, " yanglint [options] [-f { yang | yin | tree }] <file>...\n");
37 fprintf(stdout, " Validates the YANG module in <file>, and all its dependencies.\n\n");
38 fprintf(stdout, " yanglint [options] [-f { xml | json }] <schema>... <file>...\n");
39 fprintf(stdout, " Validates the YANG modeled data in <file> according to the <schema>.\n\n");
40 fprintf(stdout, " yanglint\n");
41 fprintf(stdout, " Starts interactive mode with more features.\n\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +020042
43 if (shortout) {
44 return;
45 }
46 fprintf(stdout, "Options:\n"
47 " -h, --help Show this help message and exit.\n"
48 " -v, --version Show version number and exit.\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020049 " -V, --verbose Show verbose messages, can be used multiple times to\n"
50 " increase verbosity.\n"
Michal Vasko5e5172b2017-04-04 11:00:40 +020051#ifndef NDEBUG
Michal Vasko1c8edc72017-04-03 13:49:51 +020052 " -G GROUPS, --debug=GROUPS\n"
53 " Enable printing of specific debugging message group\n"
54 " (nothing will be printed unless verbosity is set to debug):\n"
55 " <group>[,<group>]* (dict, yang, yin, xpath, diff)\n\n"
Michal Vasko5e5172b2017-04-04 11:00:40 +020056#endif
Radek Krejcie02fb702017-06-13 16:53:22 +020057 " -p PATH, --path=PATH Search path for schema (YANG/YIN) modules. The option can be used multiple times.\n"
58 " Current working directory and path of the module being added is used implicitly.\n\n"
Radek Krejci65b372b2017-03-10 11:00:10 +010059 " -s, --strict Strict data parsing (do not skip unknown data),\n"
60 " has no effect for schemas.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020061 " -f FORMAT, --format=FORMAT\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020062 " Convert to FORMAT. Supported formats: \n"
63 " tree, yin, yang for schemas,\n"
Radek Krejci922ba5a2017-08-03 11:39:00 +020064 " xml, json for data.\n"
65 " -a, --auto Modify the xml output by adding envelopes for autodetection.\n\n"
Radek Krejci819dd4b2017-03-07 15:35:48 +010066 " -i, --allimplemented Make all the imported modules implemented.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020067 " -o OUTFILE, --output=OUTFILE\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020068 " Write the output to OUTFILE instead of stdout.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020069 " -F FEATURES, --features=FEATURES\n"
70 " Features to support, default all.\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020071 " <modname>:[<feature>,]*\n\n"
72 " -d MODE, --default=MODE\n"
73 " Print data with default values, according to the MODE\n"
74 " (to print attributes, ietf-netconf-with-defaults model\n"
75 " must be loaded):\n"
76 " all - Add missing default nodes.\n"
77 " all-tagged - Add missing default nodes and mark all the default\n"
78 " nodes with the attribute.\n"
79 " trim - Remove all nodes with a default value.\n"
80 " implicit-tagged - Add missing nodes and mark them with the attribute.\n\n"
81 " -t TYPE, --type=TYPE\n"
82 " Specify data tree type in the input data file:\n"
83 " auto - Resolve data type (one of the following) automatically\n"
84 " (as pyang does) - applicable only on XML input data.\n"
85 " data - Complete datastore with status data (default type).\n"
86 " config - Configuration datastore (without status data).\n"
87 " get - Result of the NETCONF <get> operation.\n"
88 " getconfig - Result of the NETCONF <get-config> operation.\n"
Radek Krejci2d296ce2017-08-02 10:40:39 +020089 " edit - Content of the NETCONF <edit-config> operation.\n"
90 " rpc - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.\n"
Radek Krejci922ba5a2017-08-03 11:39:00 +020091 " rpcreply - Reply to the RPC. This is just a virtual TYPE, for parsing replies, 'auto' must be\n"
92 " used since the data <file>s are expected in pairs - first <file> is expected as\n"
93 " 'rpc' TYPE, the second <file> is expected as reply to the previous RPC.\n"
Radek Krejci2d296ce2017-08-02 10:40:39 +020094 " notif - Notification instance (content of the <notification> element without <eventTime>.\n\n"
Radek Krejcifeeb1ef2017-08-02 16:02:17 +020095 " -r FILE, --running=FILE\n"
96 " - Optional parameter for 'rpc' and 'notif' TYPEs, the FILE contains running\n"
97 " configuration datastore data referenced from the RPC/Notification. The same data\n"
98 " apply to all input data <file>s. Note that the file is validated as 'data' TYPE.\n\n"
Radek Krejci1770f612017-03-17 16:34:07 +010099 " -y YANGLIB_PATH - Path to a yang-library data describing the initial context.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200100 "Tree output specific options:\n"
Radek Krejcif9c28262017-01-25 11:36:02 +0100101 " --tree-help Print help on tree symbols and exit.\n"
102 " --tree-print-groupings\n"
103 " Print groupings.\n\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200104}
105
106void
107tree_help(void)
108{
109 fprintf(stdout, "Each node is printed as:\n\n");
110 fprintf(stdout, "<status> <flags> <name> <opts> <type> <if-features>\n\n"
111 " <status> is one of:\n"
112 " + for current\n"
113 " x for deprecated\n"
114 " o for obsolete\n\n"
115 " <flags> is one of:\n"
116 " rw for configuration data\n"
117 " ro for status data\n"
118 " -x for RPCs\n"
119 " -n for Notification\n\n"
120 " <name> is the name of the node\n"
121 " (<name>) means that the node is a choice node\n"
122 " :(<name>) means that the node is a case node\n\n"
123 " if the node is augmented into the tree from another module,\n"
124 " it is printed with the module name as <module-name>:<name>.\n\n"
125 " <opts> is one of:\n"
126 " ? for an optional leaf or choice\n"
127 " ! for a presence container\n"
128 " * for a leaf-list or list\n"
129 " [<keys>] for a list's keys\n\n"
130 " <type> is the name of the type for leafs and leaf-lists\n"
131 " If there is a default value defined, it is printed within\n"
132 " angle brackets <default-value>.\n"
133 " If the type is a leafref, the type is printed as -> TARGET`\n\n"
134 " <if-features> is the list of features this node depends on,\n"
135 " printed within curly brackets and a question mark {...}?\n\n");
136}
137
138void
139version(void)
140{
141 fprintf(stdout, "yanglint %d.%d.%d\n", LY_VERSION_MAJOR, LY_VERSION_MINOR, LY_VERSION_MICRO);
142}
143void
144libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
145{
Radek Krejciff5e33f2016-10-25 14:24:26 +0200146 char *levstr;
147
148 if (level <= verbose) {
149 switch(level) {
150 case LY_LLERR:
151 levstr = "err :";
152 break;
153 case LY_LLWRN:
154 levstr = "warn:";
155 break;
156 case LY_LLVRB:
157 levstr = "verb:";
158 break;
159 default:
160 levstr = "dbg :";
161 break;
162 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200163 if (path) {
Radek Krejciff5e33f2016-10-25 14:24:26 +0200164 fprintf(stderr, "%s %s (%s)\n", levstr, msg, path);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200165 } else {
Radek Krejciff5e33f2016-10-25 14:24:26 +0200166 fprintf(stderr, "%s %s\n", levstr, msg);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200167 }
168 }
169}
170
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200171/*
172 * return:
173 * 0 - error
174 * 1 - schema format
175 * 2 - data format
176 */
177static int
178get_fileformat(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
179{
180 char *ptr;
181 LYS_INFORMAT informat_s;
182 LYD_FORMAT informat_d;
183
184 /* get the file format */
185 if ((ptr = strrchr(filename, '.')) != NULL) {
186 ++ptr;
187 if (!strcmp(ptr, "yin")) {
188 informat_s = LYS_IN_YIN;
189 informat_d = 0;
190 } else if (!strcmp(ptr, "yang")) {
191 informat_s = LYS_IN_YANG;
192 informat_d = 0;
193 } else if (!strcmp(ptr, "xml")) {
194 informat_s = 0;
195 informat_d = LYD_XML;
196 } else if (!strcmp(ptr, "json")) {
197 informat_s = 0;
198 informat_d = LYD_JSON;
199 } else {
200 fprintf(stderr, "yanglint error: input file in an unknown format \"%s\".\n", ptr);
201 return 0;
202 }
203 } else {
204 fprintf(stderr, "yanglint error: input file \"%s\" without file extension - unknown format.\n", filename);
205 return 0;
206 }
207
208 if (data) {
209 (*data) = informat_d;
210 }
211 if (schema) {
212 (*schema) = informat_s;
213 }
214
215 if (informat_s) {
216 return 1;
217 } else {
218 return 2;
219 }
220}
221
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200222int
223main_ni(int argc, char* argv[])
224{
225 int ret = EXIT_FAILURE;
Radek Krejcif9c28262017-01-25 11:36:02 +0100226 int opt, opt_index = 0, i, featsize = 0, grps = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200227 struct option options[] = {
Radek Krejci922ba5a2017-08-03 11:39:00 +0200228 {"auto", no_argument, NULL, 'a'},
Radek Krejcifeb903c2016-10-27 17:29:07 +0200229 {"default", required_argument, NULL, 'd'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200230 {"format", required_argument, NULL, 'f'},
231 {"features", required_argument, NULL, 'F'},
Radek Krejcif9c28262017-01-25 11:36:02 +0100232 {"tree-print-groupings", no_argument, NULL, 'g'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200233 {"help", no_argument, NULL, 'h'},
234 {"tree-help", no_argument, NULL, 'H'},
Radek Krejci819dd4b2017-03-07 15:35:48 +0100235 {"allimplemented", no_argument, NULL, 'i'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200236 {"output", required_argument, NULL, 'o'},
237 {"path", required_argument, NULL, 'p'},
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200238 {"running", required_argument, NULL, 'r'},
Radek Krejci65b372b2017-03-10 11:00:10 +0100239 {"strict", no_argument, NULL, 's'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200240 {"version", no_argument, NULL, 'v'},
241 {"verbose", no_argument, NULL, 'V'},
Michal Vasko5e5172b2017-04-04 11:00:40 +0200242#ifndef NDEBUG
Michal Vasko1c8edc72017-04-03 13:49:51 +0200243 {"debug", required_argument, NULL, 'G'},
Michal Vasko5e5172b2017-04-04 11:00:40 +0200244#endif
Radek Krejcifeb903c2016-10-27 17:29:07 +0200245 {"type", required_argument, NULL, 't'},
Radek Krejci1770f612017-03-17 16:34:07 +0100246 {NULL, required_argument, NULL, 'y'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200247 {NULL, 0, NULL, 0}
248 };
249 FILE *out = stdout;
250 struct ly_ctx *ctx = NULL;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200251 const struct lys_module *mod;
252 LYS_OUTFORMAT outformat_s = 0;
253 LYS_INFORMAT informat_s;
Radek Krejci1770f612017-03-17 16:34:07 +0100254 LYD_FORMAT informat_d, outformat_d = 0, ylformat = 0;
Radek Krejci618405d2017-03-25 19:42:03 -0500255 struct ly_set *searchpaths = NULL;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200256 const char *running_file = NULL, *envelope_s = NULL;
Radek Krejcie02fb702017-06-13 16:53:22 +0200257 char **feat = NULL, *ptr, *featlist, *ylpath = NULL, *dir;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200258 struct stat st;
259 uint32_t u;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200260 int options_dflt = 0, options_parser = 0, options_allimplemented = 0, envelope = 0;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200261 struct dataitem {
262 const char *filename;
263 LYD_FORMAT format;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200264 int type;
265 struct lyd_node *tree;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200266 struct dataitem *next;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200267 } *data = NULL, *data_item, *data_prev = NULL;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200268 struct ly_set *mods = NULL;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200269 struct lyd_node *running = NULL;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200270 struct lyxml_elem *xml = NULL;
Radek Krejci6de1ada2017-02-27 11:26:43 +0100271 void *p;
Radek Krejcie02fb702017-06-13 16:53:22 +0200272 int index = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200273
274 opterr = 0;
Michal Vasko5e5172b2017-04-04 11:00:40 +0200275#ifndef NDEBUG
Radek Krejci922ba5a2017-08-03 11:39:00 +0200276 while ((opt = getopt_long(argc, argv, "ad:f:F:ghHio:p:r:st:vVG:y:", options, &opt_index)) != -1)
Michal Vasko5e5172b2017-04-04 11:00:40 +0200277#else
Radek Krejci922ba5a2017-08-03 11:39:00 +0200278 while ((opt = getopt_long(argc, argv, "ad:f:F:ghHio:p:r:st:vVy:", options, &opt_index)) != -1)
Michal Vasko5e5172b2017-04-04 11:00:40 +0200279#endif
280 {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200281 switch (opt) {
Radek Krejci922ba5a2017-08-03 11:39:00 +0200282 case 'a':
283 envelope = 1;
284 break;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200285 case 'd':
286 if (!strcmp(optarg, "all")) {
287 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL;
288 } else if (!strcmp(optarg, "all-tagged")) {
289 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
290 } else if (!strcmp(optarg, "trim")) {
291 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_TRIM;
292 } else if (!strcmp(optarg, "implicit-tagged")) {
293 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
294 } else {
295 fprintf(stderr, "yanglint error: unknown default mode %s\n", optarg);
296 help(1);
297 goto cleanup;
298 }
299 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200300 case 'f':
301 if (!strcasecmp(optarg, "tree")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200302 outformat_s = LYS_OUT_TREE;
303 outformat_d = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200304 } else if (!strcasecmp(optarg, "yin")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200305 outformat_s = LYS_OUT_YIN;
306 outformat_d = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200307 } else if (!strcasecmp(optarg, "yang")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200308 outformat_s = LYS_OUT_YANG;
309 outformat_d = 0;
310 } else if (!strcasecmp(optarg, "xml")) {
311 outformat_s = 0;
312 outformat_d = LYD_XML;
313 } else if (!strcasecmp(optarg, "json")) {
314 outformat_s = 0;
315 outformat_d = LYD_JSON;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200316 } else {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200317 fprintf(stderr, "yanglint error: unknown output format %s\n", optarg);
318 help(1);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200319 goto cleanup;
320 }
321 break;
322 case 'F':
323 featsize++;
324 if (!feat) {
Radek Krejci6de1ada2017-02-27 11:26:43 +0100325 p = malloc(sizeof *feat);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200326 } else {
Radek Krejci6de1ada2017-02-27 11:26:43 +0100327 p = realloc(feat, featsize * sizeof *feat);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200328 }
Radek Krejci6de1ada2017-02-27 11:26:43 +0100329 if (!p) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200330 fprintf(stderr, "yanglint error: Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
Radek Krejci6de1ada2017-02-27 11:26:43 +0100331 goto cleanup;
332 }
333 feat = p;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200334 feat[featsize - 1] = strdup(optarg);
335 ptr = strchr(feat[featsize - 1], ':');
336 if (!ptr) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200337 fprintf(stderr, "yanglint error: Invalid format of the features specification (%s)", optarg);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200338 goto cleanup;
339 }
340 *ptr = '\0';
341
342 break;
Radek Krejcif9c28262017-01-25 11:36:02 +0100343 case 'g':
344 grps = 1;
345 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200346 case 'h':
347 help(0);
348 ret = EXIT_SUCCESS;
349 goto cleanup;
350 case 'H':
351 tree_help();
352 ret = EXIT_SUCCESS;
353 goto cleanup;
Radek Krejci819dd4b2017-03-07 15:35:48 +0100354 case 'i':
355 options_allimplemented = 1;
356 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200357 case 'o':
Radek Krejcid9732442016-10-20 13:29:34 +0200358 if (out != stdout) {
359 fclose(out);
360 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200361 out = fopen(optarg, "w");
362 if (!out) {
363 fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
364 goto cleanup;
365 }
366 break;
367 case 'p':
Radek Krejci618405d2017-03-25 19:42:03 -0500368 if (stat(optarg, &st) == -1) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200369 fprintf(stderr, "yanglint error: Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200370 goto cleanup;
371 }
372 if (!S_ISDIR(st.st_mode)) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200373 fprintf(stderr, "yanglint error: Provided search path is not a directory.\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200374 goto cleanup;
375 }
Radek Krejci618405d2017-03-25 19:42:03 -0500376 if (!searchpaths) {
377 searchpaths = ly_set_new();
378 }
379 ly_set_add(searchpaths, optarg, 0);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200380 break;
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200381 case 'r':
382 if (running_file) {
383 fprintf(stderr, "yanglint error: The running datastore (-r) cannot be set multiple times.\n");
384 goto cleanup;
385 }
386 running_file = optarg;
387 break;
Radek Krejci65b372b2017-03-10 11:00:10 +0100388 case 's':
389 options_parser |= LYD_OPT_STRICT;
390 break;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200391 case 't':
392 if (!strcmp(optarg, "auto")) {
393 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
394 } else if (!strcmp(optarg, "config")) {
395 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
396 } else if (!strcmp(optarg, "get")) {
397 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
398 } else if (!strcmp(optarg, "getconfig")) {
399 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
400 } else if (!strcmp(optarg, "edit")) {
401 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
402 } else if (!strcmp(optarg, "data")) {
Radek Krejci06f8bb92017-08-02 15:36:25 +0200403 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
Radek Krejci2d296ce2017-08-02 10:40:39 +0200404 } else if (!strcmp(optarg, "rpc")) {
405 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200406 } else if (!strcmp(optarg, "rpcreply")) {
407 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
Radek Krejci2d296ce2017-08-02 10:40:39 +0200408 } else if (!strcmp(optarg, "notif")) {
409 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200410 } else {
411 fprintf(stderr, "yanglint error: unknown data tree type %s\n", optarg);
412 help(1);
413 goto cleanup;
414 }
415 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200416 case 'v':
417 version();
418 ret = EXIT_SUCCESS;
419 goto cleanup;
420 case 'V':
Radek Krejcid80c8602016-10-25 11:56:03 +0200421 verbose++;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200422 break;
Michal Vasko5e5172b2017-04-04 11:00:40 +0200423#ifndef NDEBUG
Michal Vasko1c8edc72017-04-03 13:49:51 +0200424 case 'G':
425 u = 0;
426 ptr = optarg;
427 while (ptr[0]) {
428 if (!strncmp(ptr, "dict", 4)) {
429 u |= LY_LDGDICT;
430 ptr += 4;
431 } else if (!strncmp(ptr, "yang", 4)) {
432 u |= LY_LDGYANG;
433 ptr += 4;
434 } else if (!strncmp(ptr, "yin", 3)) {
435 u |= LY_LDGYIN;
436 ptr += 3;
437 } else if (!strncmp(ptr, "xpath", 5)) {
438 u |= LY_LDGXPATH;
439 ptr += 5;
440 } else if (!strncmp(ptr, "diff", 4)) {
441 u |= LY_LDGDIFF;
442 ptr += 4;
443 }
444
445 if (ptr[0]) {
446 if (ptr[0] != ',') {
447 fprintf(stderr, "yanglint error: unknown debug group string \"%s\"\n", optarg);
448 goto cleanup;
449 }
450 ++ptr;
451 }
452 }
453 ly_verb_dbg(u);
454 break;
Michal Vasko5e5172b2017-04-04 11:00:40 +0200455#endif
Radek Krejci1770f612017-03-17 16:34:07 +0100456 case 'y':
457 ptr = strrchr(optarg, '.');
458 if (ptr) {
459 ptr++;
460 if (!strcmp(ptr, "xml")) {
461 ylformat = LYD_XML;
462 } else if (!strcmp(ptr, "json")) {
463 ylformat = LYD_JSON;
464 } else {
465 fprintf(stderr, "yanglint error: yang-library file in an unknown format \"%s\".\n", ptr);
466 goto cleanup;
467 }
468 } else {
469 fprintf(stderr, "yanglint error: yang-library file in an unknown format.\n");
470 goto cleanup;
471 }
472 ylpath = optarg;
473 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200474 default:
475 help(1);
476 if (optopt) {
477 fprintf(stderr, "yanglint error: invalid option: -%c\n", optopt);
478 } else {
479 fprintf(stderr, "yanglint error: invalid option: %s\n", argv[optind - 1]);
480 }
481 goto cleanup;
482 }
483 }
484
Radek Krejcifeb903c2016-10-27 17:29:07 +0200485 /* check options compatibility */
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200486 if (optind >= argc) {
487 help(1);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200488 fprintf(stderr, "yanglint error: missing <file> to process\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200489 goto cleanup;;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200490 }
Radek Krejci922ba5a2017-08-03 11:39:00 +0200491 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_RPCREPLY) {
492
493 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200494 if (outformat_s && outformat_s != LYS_OUT_TREE && (optind + 1) < argc) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200495 /* we have multiple schemas to be printed as YIN or YANG */
Radek Krejcifeb903c2016-10-27 17:29:07 +0200496 fprintf(stderr, "yanglint error: too many schemas to convert and store.\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200497 goto cleanup;
498 }
Radek Krejcif9c28262017-01-25 11:36:02 +0100499 if (grps) {
500 if (outformat_d || (outformat_s && outformat_s != LYS_OUT_TREE)) {
501 /* we have --tree-print-grouping with other output format than tree */
502 fprintf(stderr,
503 "yanglint warning: --tree-print-groupings option takes effect only in case of the tree output format.\n");
504 } else {
505 outformat_s = LYS_OUT_TREE_GRPS;
506 }
507 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200508 if (!outformat_d && options_dflt) {
Radek Krejcibf026072017-08-03 09:23:57 +0200509 /* we have options for printing default nodes, but data output not specified */
510 fprintf(stderr, "yanglint warning: default mode is ignored when not printing data.\n");
Radek Krejcifeb903c2016-10-27 17:29:07 +0200511 }
Radek Krejcibf026072017-08-03 09:23:57 +0200512 if (outformat_s && options_parser) {
Radek Krejci65b372b2017-03-10 11:00:10 +0100513 /* we have options for printing data tree, but output is schema */
Radek Krejcibf026072017-08-03 09:23:57 +0200514 fprintf(stderr, "yanglint warning: data parser options are ignored when printing schema.\n");
Radek Krejcifeb903c2016-10-27 17:29:07 +0200515 }
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200516 if (running_file && !(options_parser & (LYD_OPT_RPC | LYD_OPT_NOTIF))) {
517 fprintf(stderr, "yanglint warning: running datastor applies only to RPCs or Notifications.\n");
518 /* ignore running datastore file */
519 running_file = NULL;
520 }
Radek Krejci06f8bb92017-08-02 15:36:25 +0200521 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_DATA) {
522 /* add option to ignore ietf-yang-library data for implicit data type */
523 options_parser |= LYD_OPT_DATA_NO_YANGLIB;
524 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200525
526 /* set callback for printing libyang messages */
527 ly_set_log_clb(libyang_verbclb, 1);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200528
529 /* create libyang context */
Radek Krejci1770f612017-03-17 16:34:07 +0100530 if (ylpath) {
Radek Krejci618405d2017-03-25 19:42:03 -0500531 ctx = ly_ctx_new_ylpath(searchpaths ? (const char*)searchpaths->set.g[0] : NULL, ylpath, ylformat);
Radek Krejci1770f612017-03-17 16:34:07 +0100532 } else {
Radek Krejci618405d2017-03-25 19:42:03 -0500533 ctx = ly_ctx_new(NULL);
Radek Krejci1770f612017-03-17 16:34:07 +0100534 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200535 if (!ctx) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200536 goto cleanup;
537 }
538
Radek Krejci618405d2017-03-25 19:42:03 -0500539 /* set searchpaths */
540 if (searchpaths) {
541 for (u = 0; u < searchpaths->number; u++) {
542 ly_ctx_set_searchdir(ctx, (const char*)searchpaths->set.g[u]);
543 }
Radek Krejcie02fb702017-06-13 16:53:22 +0200544 index = u + 1;
Radek Krejci618405d2017-03-25 19:42:03 -0500545 }
546
Radek Krejci819dd4b2017-03-07 15:35:48 +0100547 /* set context options */
548 if (options_allimplemented) {
549 ly_ctx_set_allimplemented(ctx);
550 }
551
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200552 /* derefered setting of verbosity in libyang after context initiation */
Radek Krejciff5e33f2016-10-25 14:24:26 +0200553 ly_verb(verbose);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200554
Radek Krejcifeb903c2016-10-27 17:29:07 +0200555 mods = ly_set_new();
556
Radek Krejcie02fb702017-06-13 16:53:22 +0200557
Radek Krejcifeb903c2016-10-27 17:29:07 +0200558 /* divide input files */
559 for (i = 0; i < argc - optind; i++) {
560 /* get the file format */
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200561 if (!get_fileformat(argv[optind + i], &informat_s, &informat_d)) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200562 goto cleanup;
563 }
564
565 if (informat_s) {
566 /* load/validate schema */
567 if (verbose >= 2) {
568 fprintf(stdout, "Validating %s schema file.\n", argv[optind + i]);
569 }
Radek Krejcie02fb702017-06-13 16:53:22 +0200570 dir = strdup(argv[optind + i]);
571 ly_ctx_set_searchdir(ctx, dirname(dir));
Radek Krejcifeb903c2016-10-27 17:29:07 +0200572 mod = lys_parse_path(ctx, argv[optind + i], informat_s);
Radek Krejcie02fb702017-06-13 16:53:22 +0200573 ly_ctx_unset_searchdirs(ctx, index);
574 free(dir);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200575 if (!mod) {
576 goto cleanup;
577 }
578 ly_set_add(mods, (void *)mod, 0);
579 } else {
580 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK && informat_d != LYD_XML) {
581 /* data file content autodetection is possible only for XML input */
582 fprintf(stderr, "yanglint error: data type autodetection is applicable only to XML files.\n");
583 goto cleanup;
584 }
585
586 /* remember data filename and its format */
587 if (!data) {
588 data = data_item = malloc(sizeof *data);
589 } else {
590 for (data_item = data; data_item->next; data_item = data_item->next);
591 data_item->next = malloc(sizeof *data_item);
592 data_item = data_item->next;
593 }
594 data_item->filename = argv[optind + i];
595 data_item->format = informat_d;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200596 data_item->type = options_parser & LYD_OPT_TYPEMASK;
597 data_item->tree = NULL;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200598 data_item->next = NULL;
599 }
600 }
601
602 if (outformat_d && !data) {
603 fprintf(stderr, "yanglint error: no input data file for the specified data output format.\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200604 goto cleanup;
605 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200606
607 /* enable specified features, if not specified, all the module's features are enabled */
608 u = 4; /* skip internal libyang modules */
609 while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
610 for (i = 0; i < featsize; i++) {
611 if (!strcmp(feat[i], mod->name)) {
612 /* parse features spec */
613 featlist = strdup(feat[i] + strlen(feat[i]) + 1);
614 ptr = NULL;
615 while((ptr = strtok(ptr ? NULL : featlist, ","))) {
616 if (verbose >= 2) {
617 fprintf(stdout, "Enabling feature %s in module %s.\n", ptr, mod->name);
618 }
619 if (lys_features_enable(mod, ptr)) {
620 fprintf(stderr, "Feature %s not defined in module %s.\n", ptr, mod->name);
621 }
622 }
623 free(featlist);
624 break;
625 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200626 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200627 if (i == featsize) {
628 if (verbose >= 2) {
629 fprintf(stdout, "Enabling all features in module %s.\n", mod->name);
630 }
631 lys_features_enable(mod, "*");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200632 }
633 }
634
Radek Krejcifeb903c2016-10-27 17:29:07 +0200635 /* convert (print) to FORMAT */
636 if (outformat_s) {
637 for (u = 0; u < mods->number; u++) {
638 lys_print_file(out, (struct lys_module *)mods->set.g[u], outformat_s, NULL);
639 }
Radek Krejcie14d2eb2016-11-04 17:35:47 +0100640 } else if (data) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200641 ly_errno = 0;
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200642
643 /* prepare running datastore when specified for RPC/Notification */
644 if (running_file) {
645 /* get the file format */
646 if (!get_fileformat(running_file, NULL, &informat_d)) {
647 goto cleanup;
648 } else if (!informat_d) {
649 fprintf(stderr, "yanglint error: The running data are expected in XML or JSON format.\n");
650 goto cleanup;
651 }
652 running = lyd_parse_path(ctx, running_file, informat_d, LYD_OPT_DATA_NO_YANGLIB);
653 if (!running) {
654 fprintf(stderr, "yanglint error: Failed to parse the running datastore file for RPC/Notification validation.\n");
655 goto cleanup;
656 }
657 }
658
Radek Krejci922ba5a2017-08-03 11:39:00 +0200659 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 +0200660 /* parse data file - via LYD_OPT_TRUSTED postpone validation when all data are loaded and merged */
661 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
662 /* automatically detect data type from the data top level */
663 xml = lyxml_parse_path(ctx, data_item->filename, 0);
664 if (!xml) {
665 fprintf(stderr, "yanglint error: parsing XML data for data type autodetection failed.\n");
666 goto cleanup;
667 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200668
Radek Krejcifeb903c2016-10-27 17:29:07 +0200669 /* NOTE: namespace is ignored to simplify usage of this feature */
670 if (!strcmp(xml->name, "data")) {
671 if (verbose >= 2) {
672 fprintf(stdout, "Parsing %s as complete datastore.\n", data_item->filename);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200673 }
Radek Krejci06f8bb92017-08-02 15:36:25 +0200674 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200675 data_item->type = LYD_OPT_DATA;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200676 } else if (!strcmp(xml->name, "config")) {
677 if (verbose >= 2) {
678 fprintf(stdout, "Parsing %s as config data.\n", data_item->filename);
679 }
680 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200681 data_item->type = LYD_OPT_CONFIG;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200682 } else if (!strcmp(xml->name, "get-reply")) {
683 if (verbose >= 2) {
684 fprintf(stdout, "Parsing %s as <get> reply data.\n", data_item->filename);
685 }
686 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200687 data_item->type = LYD_OPT_GET;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200688 } else if (!strcmp(xml->name, "get-config-reply")) {
689 if (verbose >= 2) {
690 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", data_item->filename);
691 }
692 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200693 data_item->type = LYD_OPT_GETCONFIG;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200694 } else if (!strcmp(xml->name, "edit-config")) {
695 if (verbose >= 2) {
696 fprintf(stdout, "Parsing %s as <edit-config> data.\n", data_item->filename);
697 }
698 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200699 data_item->type = LYD_OPT_EDIT;
Radek Krejci2d296ce2017-08-02 10:40:39 +0200700 } else if (!strcmp(xml->name, "rpc")) {
701 if (verbose >= 2) {
702 fprintf(stdout, "Parsing %s as <rpc> data.\n", data_item->filename);
703 }
704 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200705 data_item->type = LYD_OPT_RPC;
706 } else if (!strcmp(xml->name, "rpc-reply")) {
707 if (verbose >= 2) {
708 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", data_item->filename);
709 }
710 if (!data_prev || data_prev->type != LYD_OPT_RPC) {
711 fprintf(stderr, "RPC reply (%s) must be coupled with the original RPC, see help.\n", data_item->filename);
712 goto cleanup;
713 }
714 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
715 data_item->type = LYD_OPT_RPCREPLY;
Radek Krejci2d296ce2017-08-02 10:40:39 +0200716 } else if (!strcmp(xml->name, "notification")) {
717 if (verbose >= 2) {
718 fprintf(stdout, "Parsing %s as <notification> data.\n", data_item->filename);
719 }
720 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200721 data_item->type = LYD_OPT_NOTIF;
Radek Krejci9604a662017-08-03 13:31:50 +0200722
723 /* ignore eventTime element if present */
724 while (xml->child && !strcmp(xml->child->name, "eventTime")) {
725 lyxml_free(ctx, xml->child);
726 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200727 } else {
728 fprintf(stderr, "yanglint error: invalid top-level element \"%s\" for data type autodetection.\n",
729 xml->name);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200730 goto cleanup;
731 }
732
Radek Krejci922ba5a2017-08-03 11:39:00 +0200733 if (data_item->type == LYD_OPT_RPCREPLY) {
734 data_item->tree = lyd_parse_xml(ctx, &xml->child, options_parser, data_prev->tree, running);
735 } else {
736 data_item->tree = lyd_parse_xml(ctx, &xml->child, options_parser, running);
737 }
738 options_parser |= LYD_OPT_TYPEMASK;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200739 } else {
Radek Krejci922ba5a2017-08-03 11:39:00 +0200740 data_item->tree = lyd_parse_path(ctx, data_item->filename, data_item->format, options_parser, running);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200741 }
742 if (ly_errno) {
743 goto cleanup;
744 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200745 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200746
Radek Krejcifeb903c2016-10-27 17:29:07 +0200747 /* print only if data output format specified */
Radek Krejci922ba5a2017-08-03 11:39:00 +0200748 if (outformat_d) {
749 for (data_item = data; data_item; data_item = data_item->next) {
750 if (verbose >= 2) {
751 fprintf(stdout, "File %s:\n", data_item->filename);
752 }
753 if (outformat_d == LYD_XML && envelope) {
754 switch (data_item->type) {
755 case LYD_OPT_DATA:
756 envelope_s = "data";
757 break;
758 case LYD_OPT_CONFIG:
759 envelope_s = "config";
760 break;
761 case LYD_OPT_GET:
762 envelope_s = "get-reply";
763 break;
764 case LYD_OPT_GETCONFIG:
765 envelope_s = "get-config-reply";
766 break;
767 case LYD_OPT_EDIT:
768 envelope_s = "edit-config";
769 break;
770 case LYD_OPT_RPC:
771 envelope_s = "rpc";
772 break;
773 case LYD_OPT_RPCREPLY:
774 envelope_s = "rpc-reply";
775 break;
776 case LYD_OPT_NOTIF:
777 envelope_s = "notification";
778 break;
779 }
780 fprintf(out, "<%s>\n", envelope_s);
781 if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
782 /* action */
783 fprintf(out, "<action xmlns=\"urn:ietf:params:xml:ns:yang:1\">\n");
784 }
785 }
786 lyd_print_file(out, (data_item->type == LYD_OPT_RPCREPLY) ? data_item->tree->child : data_item->tree,
787 outformat_d, LYP_WITHSIBLINGS | LYP_FORMAT | options_dflt);
788 if (envelope_s) {
789 if (data_item->type == LYD_OPT_RPC && data_item->tree->schema->nodetype != LYS_RPC) {
790 fprintf(out, "</action>\n");
791 }
792 fprintf(out, "</%s>\n", envelope_s);
793 }
794 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200795 }
796 }
797
798 ret = EXIT_SUCCESS;
799
800cleanup:
Radek Krejci4fd7ef22016-10-20 13:26:41 +0200801 if (out && out != stdout) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200802 fclose(out);
803 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200804 ly_set_free(mods);
Radek Krejci618405d2017-03-25 19:42:03 -0500805 ly_set_free(searchpaths);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200806 for (i = 0; i < featsize; i++) {
807 free(feat[i]);
808 }
809 free(feat);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200810 for (; data; data = data_item) {
811 data_item = data->next;
Radek Krejci922ba5a2017-08-03 11:39:00 +0200812 lyd_free_withsiblings(data->tree);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200813 free(data);
814 }
815 lyxml_free(ctx, xml);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200816 ly_ctx_destroy(ctx, NULL);
817
818 return ret;
819}