blob: e45cc8f5dcac345628efb5aac4dbaf01202fde70 [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"
64 " xml, json for data.\n\n"
Radek Krejci819dd4b2017-03-07 15:35:48 +010065 " -i, --allimplemented Make all the imported modules implemented.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020066 " -o OUTFILE, --output=OUTFILE\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020067 " Write the output to OUTFILE instead of stdout.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020068 " -F FEATURES, --features=FEATURES\n"
69 " Features to support, default all.\n"
Radek Krejcifeb903c2016-10-27 17:29:07 +020070 " <modname>:[<feature>,]*\n\n"
71 " -d MODE, --default=MODE\n"
72 " Print data with default values, according to the MODE\n"
73 " (to print attributes, ietf-netconf-with-defaults model\n"
74 " must be loaded):\n"
75 " all - Add missing default nodes.\n"
76 " all-tagged - Add missing default nodes and mark all the default\n"
77 " nodes with the attribute.\n"
78 " trim - Remove all nodes with a default value.\n"
79 " implicit-tagged - Add missing nodes and mark them with the attribute.\n\n"
80 " -t TYPE, --type=TYPE\n"
81 " Specify data tree type in the input data file:\n"
82 " auto - Resolve data type (one of the following) automatically\n"
83 " (as pyang does) - applicable only on XML input data.\n"
84 " data - Complete datastore with status data (default type).\n"
85 " config - Configuration datastore (without status data).\n"
86 " get - Result of the NETCONF <get> operation.\n"
87 " getconfig - Result of the NETCONF <get-config> operation.\n"
Radek Krejci2d296ce2017-08-02 10:40:39 +020088 " edit - Content of the NETCONF <edit-config> operation.\n"
89 " rpc - Content of the NETCONF <rpc> message, defined as YANG's rpc input statement.\n"
90 " notif - Notification instance (content of the <notification> element without <eventTime>.\n\n"
Radek Krejcifeeb1ef2017-08-02 16:02:17 +020091 " -r FILE, --running=FILE\n"
92 " - Optional parameter for 'rpc' and 'notif' TYPEs, the FILE contains running\n"
93 " configuration datastore data referenced from the RPC/Notification. The same data\n"
94 " 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 +010095 " -y YANGLIB_PATH - Path to a yang-library data describing the initial context.\n\n"
Radek Krejci6ef5efc2016-10-10 16:38:49 +020096 "Tree output specific options:\n"
Radek Krejcif9c28262017-01-25 11:36:02 +010097 " --tree-help Print help on tree symbols and exit.\n"
98 " --tree-print-groupings\n"
99 " Print groupings.\n\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200100}
101
102void
103tree_help(void)
104{
105 fprintf(stdout, "Each node is printed as:\n\n");
106 fprintf(stdout, "<status> <flags> <name> <opts> <type> <if-features>\n\n"
107 " <status> is one of:\n"
108 " + for current\n"
109 " x for deprecated\n"
110 " o for obsolete\n\n"
111 " <flags> is one of:\n"
112 " rw for configuration data\n"
113 " ro for status data\n"
114 " -x for RPCs\n"
115 " -n for Notification\n\n"
116 " <name> is the name of the node\n"
117 " (<name>) means that the node is a choice node\n"
118 " :(<name>) means that the node is a case node\n\n"
119 " if the node is augmented into the tree from another module,\n"
120 " it is printed with the module name as <module-name>:<name>.\n\n"
121 " <opts> is one of:\n"
122 " ? for an optional leaf or choice\n"
123 " ! for a presence container\n"
124 " * for a leaf-list or list\n"
125 " [<keys>] for a list's keys\n\n"
126 " <type> is the name of the type for leafs and leaf-lists\n"
127 " If there is a default value defined, it is printed within\n"
128 " angle brackets <default-value>.\n"
129 " If the type is a leafref, the type is printed as -> TARGET`\n\n"
130 " <if-features> is the list of features this node depends on,\n"
131 " printed within curly brackets and a question mark {...}?\n\n");
132}
133
134void
135version(void)
136{
137 fprintf(stdout, "yanglint %d.%d.%d\n", LY_VERSION_MAJOR, LY_VERSION_MINOR, LY_VERSION_MICRO);
138}
139void
140libyang_verbclb(LY_LOG_LEVEL level, const char *msg, const char *path)
141{
Radek Krejciff5e33f2016-10-25 14:24:26 +0200142 char *levstr;
143
144 if (level <= verbose) {
145 switch(level) {
146 case LY_LLERR:
147 levstr = "err :";
148 break;
149 case LY_LLWRN:
150 levstr = "warn:";
151 break;
152 case LY_LLVRB:
153 levstr = "verb:";
154 break;
155 default:
156 levstr = "dbg :";
157 break;
158 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200159 if (path) {
Radek Krejciff5e33f2016-10-25 14:24:26 +0200160 fprintf(stderr, "%s %s (%s)\n", levstr, msg, path);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200161 } else {
Radek Krejciff5e33f2016-10-25 14:24:26 +0200162 fprintf(stderr, "%s %s\n", levstr, msg);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200163 }
164 }
165}
166
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200167/*
168 * return:
169 * 0 - error
170 * 1 - schema format
171 * 2 - data format
172 */
173static int
174get_fileformat(const char *filename, LYS_INFORMAT *schema, LYD_FORMAT *data)
175{
176 char *ptr;
177 LYS_INFORMAT informat_s;
178 LYD_FORMAT informat_d;
179
180 /* get the file format */
181 if ((ptr = strrchr(filename, '.')) != NULL) {
182 ++ptr;
183 if (!strcmp(ptr, "yin")) {
184 informat_s = LYS_IN_YIN;
185 informat_d = 0;
186 } else if (!strcmp(ptr, "yang")) {
187 informat_s = LYS_IN_YANG;
188 informat_d = 0;
189 } else if (!strcmp(ptr, "xml")) {
190 informat_s = 0;
191 informat_d = LYD_XML;
192 } else if (!strcmp(ptr, "json")) {
193 informat_s = 0;
194 informat_d = LYD_JSON;
195 } else {
196 fprintf(stderr, "yanglint error: input file in an unknown format \"%s\".\n", ptr);
197 return 0;
198 }
199 } else {
200 fprintf(stderr, "yanglint error: input file \"%s\" without file extension - unknown format.\n", filename);
201 return 0;
202 }
203
204 if (data) {
205 (*data) = informat_d;
206 }
207 if (schema) {
208 (*schema) = informat_s;
209 }
210
211 if (informat_s) {
212 return 1;
213 } else {
214 return 2;
215 }
216}
217
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200218int
219main_ni(int argc, char* argv[])
220{
221 int ret = EXIT_FAILURE;
Radek Krejcif9c28262017-01-25 11:36:02 +0100222 int opt, opt_index = 0, i, featsize = 0, grps = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200223 struct option options[] = {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200224 {"default", required_argument, NULL, 'd'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200225 {"format", required_argument, NULL, 'f'},
226 {"features", required_argument, NULL, 'F'},
Radek Krejcif9c28262017-01-25 11:36:02 +0100227 {"tree-print-groupings", no_argument, NULL, 'g'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200228 {"help", no_argument, NULL, 'h'},
229 {"tree-help", no_argument, NULL, 'H'},
Radek Krejci819dd4b2017-03-07 15:35:48 +0100230 {"allimplemented", no_argument, NULL, 'i'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200231 {"output", required_argument, NULL, 'o'},
232 {"path", required_argument, NULL, 'p'},
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200233 {"running", required_argument, NULL, 'r'},
Radek Krejci65b372b2017-03-10 11:00:10 +0100234 {"strict", no_argument, NULL, 's'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200235 {"version", no_argument, NULL, 'v'},
236 {"verbose", no_argument, NULL, 'V'},
Michal Vasko5e5172b2017-04-04 11:00:40 +0200237#ifndef NDEBUG
Michal Vasko1c8edc72017-04-03 13:49:51 +0200238 {"debug", required_argument, NULL, 'G'},
Michal Vasko5e5172b2017-04-04 11:00:40 +0200239#endif
Radek Krejcifeb903c2016-10-27 17:29:07 +0200240 {"type", required_argument, NULL, 't'},
Radek Krejci1770f612017-03-17 16:34:07 +0100241 {NULL, required_argument, NULL, 'y'},
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200242 {NULL, 0, NULL, 0}
243 };
244 FILE *out = stdout;
245 struct ly_ctx *ctx = NULL;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200246 const struct lys_module *mod;
247 LYS_OUTFORMAT outformat_s = 0;
248 LYS_INFORMAT informat_s;
Radek Krejci1770f612017-03-17 16:34:07 +0100249 LYD_FORMAT informat_d, outformat_d = 0, ylformat = 0;
Radek Krejci618405d2017-03-25 19:42:03 -0500250 struct ly_set *searchpaths = NULL;
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200251 const char *running_file = NULL;
Radek Krejcie02fb702017-06-13 16:53:22 +0200252 char **feat = NULL, *ptr, *featlist, *ylpath = NULL, *dir;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200253 struct stat st;
254 uint32_t u;
Radek Krejci819dd4b2017-03-07 15:35:48 +0100255 int options_dflt = 0, options_parser = 0, options_allimplemented = 0;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200256 struct dataitem {
257 const char *filename;
258 LYD_FORMAT format;
259 struct dataitem *next;
260 } *data = NULL, *data_item;
261 struct ly_set *mods = NULL;
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200262 struct lyd_node *root = NULL, *node = NULL, *running = NULL;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200263 struct lyxml_elem *xml = NULL;
Radek Krejci6de1ada2017-02-27 11:26:43 +0100264 void *p;
Radek Krejcie02fb702017-06-13 16:53:22 +0200265 int index = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200266
267 opterr = 0;
Michal Vasko5e5172b2017-04-04 11:00:40 +0200268#ifndef NDEBUG
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200269 while ((opt = getopt_long(argc, argv, "d:f:F:ghHio:p:r:st:vVG:y:", options, &opt_index)) != -1)
Michal Vasko5e5172b2017-04-04 11:00:40 +0200270#else
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200271 while ((opt = getopt_long(argc, argv, "d:f:F:ghHio:p:r:st:vVy:", options, &opt_index)) != -1)
Michal Vasko5e5172b2017-04-04 11:00:40 +0200272#endif
273 {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200274 switch (opt) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200275 case 'd':
276 if (!strcmp(optarg, "all")) {
277 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL;
278 } else if (!strcmp(optarg, "all-tagged")) {
279 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
280 } else if (!strcmp(optarg, "trim")) {
281 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_TRIM;
282 } else if (!strcmp(optarg, "implicit-tagged")) {
283 options_dflt = (options_dflt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
284 } else {
285 fprintf(stderr, "yanglint error: unknown default mode %s\n", optarg);
286 help(1);
287 goto cleanup;
288 }
289 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200290 case 'f':
291 if (!strcasecmp(optarg, "tree")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200292 outformat_s = LYS_OUT_TREE;
293 outformat_d = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200294 } else if (!strcasecmp(optarg, "yin")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200295 outformat_s = LYS_OUT_YIN;
296 outformat_d = 0;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200297 } else if (!strcasecmp(optarg, "yang")) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200298 outformat_s = LYS_OUT_YANG;
299 outformat_d = 0;
300 } else if (!strcasecmp(optarg, "xml")) {
301 outformat_s = 0;
302 outformat_d = LYD_XML;
303 } else if (!strcasecmp(optarg, "json")) {
304 outformat_s = 0;
305 outformat_d = LYD_JSON;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200306 } else {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200307 fprintf(stderr, "yanglint error: unknown output format %s\n", optarg);
308 help(1);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200309 goto cleanup;
310 }
311 break;
312 case 'F':
313 featsize++;
314 if (!feat) {
Radek Krejci6de1ada2017-02-27 11:26:43 +0100315 p = malloc(sizeof *feat);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200316 } else {
Radek Krejci6de1ada2017-02-27 11:26:43 +0100317 p = realloc(feat, featsize * sizeof *feat);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200318 }
Radek Krejci6de1ada2017-02-27 11:26:43 +0100319 if (!p) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200320 fprintf(stderr, "yanglint error: Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
Radek Krejci6de1ada2017-02-27 11:26:43 +0100321 goto cleanup;
322 }
323 feat = p;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200324 feat[featsize - 1] = strdup(optarg);
325 ptr = strchr(feat[featsize - 1], ':');
326 if (!ptr) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200327 fprintf(stderr, "yanglint error: Invalid format of the features specification (%s)", optarg);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200328 goto cleanup;
329 }
330 *ptr = '\0';
331
332 break;
Radek Krejcif9c28262017-01-25 11:36:02 +0100333 case 'g':
334 grps = 1;
335 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200336 case 'h':
337 help(0);
338 ret = EXIT_SUCCESS;
339 goto cleanup;
340 case 'H':
341 tree_help();
342 ret = EXIT_SUCCESS;
343 goto cleanup;
Radek Krejci819dd4b2017-03-07 15:35:48 +0100344 case 'i':
345 options_allimplemented = 1;
346 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200347 case 'o':
Radek Krejcid9732442016-10-20 13:29:34 +0200348 if (out != stdout) {
349 fclose(out);
350 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200351 out = fopen(optarg, "w");
352 if (!out) {
353 fprintf(stderr, "yanglint error: unable open output file %s (%s)\n", optarg, strerror(errno));
354 goto cleanup;
355 }
356 break;
357 case 'p':
Radek Krejci618405d2017-03-25 19:42:03 -0500358 if (stat(optarg, &st) == -1) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200359 fprintf(stderr, "yanglint error: Unable to use search path (%s) - %s.\n", optarg, strerror(errno));
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200360 goto cleanup;
361 }
362 if (!S_ISDIR(st.st_mode)) {
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200363 fprintf(stderr, "yanglint error: Provided search path is not a directory.\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200364 goto cleanup;
365 }
Radek Krejci618405d2017-03-25 19:42:03 -0500366 if (!searchpaths) {
367 searchpaths = ly_set_new();
368 }
369 ly_set_add(searchpaths, optarg, 0);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200370 break;
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200371 case 'r':
372 if (running_file) {
373 fprintf(stderr, "yanglint error: The running datastore (-r) cannot be set multiple times.\n");
374 goto cleanup;
375 }
376 running_file = optarg;
377 break;
Radek Krejci65b372b2017-03-10 11:00:10 +0100378 case 's':
379 options_parser |= LYD_OPT_STRICT;
380 break;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200381 case 't':
382 if (!strcmp(optarg, "auto")) {
383 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
384 } else if (!strcmp(optarg, "config")) {
385 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
386 } else if (!strcmp(optarg, "get")) {
387 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
388 } else if (!strcmp(optarg, "getconfig")) {
389 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
390 } else if (!strcmp(optarg, "edit")) {
391 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
392 } else if (!strcmp(optarg, "data")) {
Radek Krejci06f8bb92017-08-02 15:36:25 +0200393 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
Radek Krejci2d296ce2017-08-02 10:40:39 +0200394 } else if (!strcmp(optarg, "rpc")) {
395 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
396 } else if (!strcmp(optarg, "notif")) {
397 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200398 } else {
399 fprintf(stderr, "yanglint error: unknown data tree type %s\n", optarg);
400 help(1);
401 goto cleanup;
402 }
403 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200404 case 'v':
405 version();
406 ret = EXIT_SUCCESS;
407 goto cleanup;
408 case 'V':
Radek Krejcid80c8602016-10-25 11:56:03 +0200409 verbose++;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200410 break;
Michal Vasko5e5172b2017-04-04 11:00:40 +0200411#ifndef NDEBUG
Michal Vasko1c8edc72017-04-03 13:49:51 +0200412 case 'G':
413 u = 0;
414 ptr = optarg;
415 while (ptr[0]) {
416 if (!strncmp(ptr, "dict", 4)) {
417 u |= LY_LDGDICT;
418 ptr += 4;
419 } else if (!strncmp(ptr, "yang", 4)) {
420 u |= LY_LDGYANG;
421 ptr += 4;
422 } else if (!strncmp(ptr, "yin", 3)) {
423 u |= LY_LDGYIN;
424 ptr += 3;
425 } else if (!strncmp(ptr, "xpath", 5)) {
426 u |= LY_LDGXPATH;
427 ptr += 5;
428 } else if (!strncmp(ptr, "diff", 4)) {
429 u |= LY_LDGDIFF;
430 ptr += 4;
431 }
432
433 if (ptr[0]) {
434 if (ptr[0] != ',') {
435 fprintf(stderr, "yanglint error: unknown debug group string \"%s\"\n", optarg);
436 goto cleanup;
437 }
438 ++ptr;
439 }
440 }
441 ly_verb_dbg(u);
442 break;
Michal Vasko5e5172b2017-04-04 11:00:40 +0200443#endif
Radek Krejci1770f612017-03-17 16:34:07 +0100444 case 'y':
445 ptr = strrchr(optarg, '.');
446 if (ptr) {
447 ptr++;
448 if (!strcmp(ptr, "xml")) {
449 ylformat = LYD_XML;
450 } else if (!strcmp(ptr, "json")) {
451 ylformat = LYD_JSON;
452 } else {
453 fprintf(stderr, "yanglint error: yang-library file in an unknown format \"%s\".\n", ptr);
454 goto cleanup;
455 }
456 } else {
457 fprintf(stderr, "yanglint error: yang-library file in an unknown format.\n");
458 goto cleanup;
459 }
460 ylpath = optarg;
461 break;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200462 default:
463 help(1);
464 if (optopt) {
465 fprintf(stderr, "yanglint error: invalid option: -%c\n", optopt);
466 } else {
467 fprintf(stderr, "yanglint error: invalid option: %s\n", argv[optind - 1]);
468 }
469 goto cleanup;
470 }
471 }
472
Radek Krejcifeb903c2016-10-27 17:29:07 +0200473 /* check options compatibility */
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200474 if (optind >= argc) {
475 help(1);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200476 fprintf(stderr, "yanglint error: missing <file> to process\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200477 goto cleanup;;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200478 }
479 if (outformat_s && outformat_s != LYS_OUT_TREE && (optind + 1) < argc) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200480 /* we have multiple schemas to be printed as YIN or YANG */
Radek Krejcifeb903c2016-10-27 17:29:07 +0200481 fprintf(stderr, "yanglint error: too many schemas to convert and store.\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200482 goto cleanup;
483 }
Radek Krejcif9c28262017-01-25 11:36:02 +0100484 if (grps) {
485 if (outformat_d || (outformat_s && outformat_s != LYS_OUT_TREE)) {
486 /* we have --tree-print-grouping with other output format than tree */
487 fprintf(stderr,
488 "yanglint warning: --tree-print-groupings option takes effect only in case of the tree output format.\n");
489 } else {
490 outformat_s = LYS_OUT_TREE_GRPS;
491 }
492 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200493 if (!outformat_d && options_dflt) {
494 /* we have options for printing default nodes, but output is schema */
495 fprintf(stderr, "yanglint warning: default mode is ignored when printing schema.\n");
496 }
497 if (!outformat_d && options_parser) {
Radek Krejci65b372b2017-03-10 11:00:10 +0100498 /* we have options for printing data tree, but output is schema */
Radek Krejcifeb903c2016-10-27 17:29:07 +0200499 fprintf(stderr, "yanglint warning: parser option is ignored when printing schema.\n");
500 }
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200501 if (running_file && !(options_parser & (LYD_OPT_RPC | LYD_OPT_NOTIF))) {
502 fprintf(stderr, "yanglint warning: running datastor applies only to RPCs or Notifications.\n");
503 /* ignore running datastore file */
504 running_file = NULL;
505 }
Radek Krejci06f8bb92017-08-02 15:36:25 +0200506 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_DATA) {
507 /* add option to ignore ietf-yang-library data for implicit data type */
508 options_parser |= LYD_OPT_DATA_NO_YANGLIB;
509 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200510
511 /* set callback for printing libyang messages */
512 ly_set_log_clb(libyang_verbclb, 1);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200513
514 /* create libyang context */
Radek Krejci1770f612017-03-17 16:34:07 +0100515 if (ylpath) {
Radek Krejci618405d2017-03-25 19:42:03 -0500516 ctx = ly_ctx_new_ylpath(searchpaths ? (const char*)searchpaths->set.g[0] : NULL, ylpath, ylformat);
Radek Krejci1770f612017-03-17 16:34:07 +0100517 } else {
Radek Krejci618405d2017-03-25 19:42:03 -0500518 ctx = ly_ctx_new(NULL);
Radek Krejci1770f612017-03-17 16:34:07 +0100519 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200520 if (!ctx) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200521 goto cleanup;
522 }
523
Radek Krejci618405d2017-03-25 19:42:03 -0500524 /* set searchpaths */
525 if (searchpaths) {
526 for (u = 0; u < searchpaths->number; u++) {
527 ly_ctx_set_searchdir(ctx, (const char*)searchpaths->set.g[u]);
528 }
Radek Krejcie02fb702017-06-13 16:53:22 +0200529 index = u + 1;
Radek Krejci618405d2017-03-25 19:42:03 -0500530 }
531
Radek Krejci819dd4b2017-03-07 15:35:48 +0100532 /* set context options */
533 if (options_allimplemented) {
534 ly_ctx_set_allimplemented(ctx);
535 }
536
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200537 /* derefered setting of verbosity in libyang after context initiation */
Radek Krejciff5e33f2016-10-25 14:24:26 +0200538 ly_verb(verbose);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200539
Radek Krejcifeb903c2016-10-27 17:29:07 +0200540 mods = ly_set_new();
541
Radek Krejcie02fb702017-06-13 16:53:22 +0200542
Radek Krejcifeb903c2016-10-27 17:29:07 +0200543 /* divide input files */
544 for (i = 0; i < argc - optind; i++) {
545 /* get the file format */
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200546 if (!get_fileformat(argv[optind + i], &informat_s, &informat_d)) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200547 goto cleanup;
548 }
549
550 if (informat_s) {
551 /* load/validate schema */
552 if (verbose >= 2) {
553 fprintf(stdout, "Validating %s schema file.\n", argv[optind + i]);
554 }
Radek Krejcie02fb702017-06-13 16:53:22 +0200555 dir = strdup(argv[optind + i]);
556 ly_ctx_set_searchdir(ctx, dirname(dir));
Radek Krejcifeb903c2016-10-27 17:29:07 +0200557 mod = lys_parse_path(ctx, argv[optind + i], informat_s);
Radek Krejcie02fb702017-06-13 16:53:22 +0200558 ly_ctx_unset_searchdirs(ctx, index);
559 free(dir);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200560 if (!mod) {
561 goto cleanup;
562 }
563 ly_set_add(mods, (void *)mod, 0);
564 } else {
565 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK && informat_d != LYD_XML) {
566 /* data file content autodetection is possible only for XML input */
567 fprintf(stderr, "yanglint error: data type autodetection is applicable only to XML files.\n");
568 goto cleanup;
569 }
570
571 /* remember data filename and its format */
572 if (!data) {
573 data = data_item = malloc(sizeof *data);
574 } else {
575 for (data_item = data; data_item->next; data_item = data_item->next);
576 data_item->next = malloc(sizeof *data_item);
577 data_item = data_item->next;
578 }
579 data_item->filename = argv[optind + i];
580 data_item->format = informat_d;
581 data_item->next = NULL;
582 }
583 }
584
585 if (outformat_d && !data) {
586 fprintf(stderr, "yanglint error: no input data file for the specified data output format.\n");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200587 goto cleanup;
588 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200589
590 /* enable specified features, if not specified, all the module's features are enabled */
591 u = 4; /* skip internal libyang modules */
592 while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
593 for (i = 0; i < featsize; i++) {
594 if (!strcmp(feat[i], mod->name)) {
595 /* parse features spec */
596 featlist = strdup(feat[i] + strlen(feat[i]) + 1);
597 ptr = NULL;
598 while((ptr = strtok(ptr ? NULL : featlist, ","))) {
599 if (verbose >= 2) {
600 fprintf(stdout, "Enabling feature %s in module %s.\n", ptr, mod->name);
601 }
602 if (lys_features_enable(mod, ptr)) {
603 fprintf(stderr, "Feature %s not defined in module %s.\n", ptr, mod->name);
604 }
605 }
606 free(featlist);
607 break;
608 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200609 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200610 if (i == featsize) {
611 if (verbose >= 2) {
612 fprintf(stdout, "Enabling all features in module %s.\n", mod->name);
613 }
614 lys_features_enable(mod, "*");
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200615 }
616 }
617
Radek Krejcifeb903c2016-10-27 17:29:07 +0200618 /* convert (print) to FORMAT */
619 if (outformat_s) {
620 for (u = 0; u < mods->number; u++) {
621 lys_print_file(out, (struct lys_module *)mods->set.g[u], outformat_s, NULL);
622 }
Radek Krejcie14d2eb2016-11-04 17:35:47 +0100623 } else if (data) {
Radek Krejcifeb903c2016-10-27 17:29:07 +0200624 ly_errno = 0;
Radek Krejcifeeb1ef2017-08-02 16:02:17 +0200625
626 /* prepare running datastore when specified for RPC/Notification */
627 if (running_file) {
628 /* get the file format */
629 if (!get_fileformat(running_file, NULL, &informat_d)) {
630 goto cleanup;
631 } else if (!informat_d) {
632 fprintf(stderr, "yanglint error: The running data are expected in XML or JSON format.\n");
633 goto cleanup;
634 }
635 running = lyd_parse_path(ctx, running_file, informat_d, LYD_OPT_DATA_NO_YANGLIB);
636 if (!running) {
637 fprintf(stderr, "yanglint error: Failed to parse the running datastore file for RPC/Notification validation.\n");
638 goto cleanup;
639 }
640 }
641
Radek Krejcifeb903c2016-10-27 17:29:07 +0200642 for (data_item = data; data_item; data_item = data_item->next) {
643 /* parse data file - via LYD_OPT_TRUSTED postpone validation when all data are loaded and merged */
644 if ((options_parser & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
645 /* automatically detect data type from the data top level */
646 xml = lyxml_parse_path(ctx, data_item->filename, 0);
647 if (!xml) {
648 fprintf(stderr, "yanglint error: parsing XML data for data type autodetection failed.\n");
649 goto cleanup;
650 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200651
Radek Krejcifeb903c2016-10-27 17:29:07 +0200652 /* NOTE: namespace is ignored to simplify usage of this feature */
653 if (!strcmp(xml->name, "data")) {
654 if (verbose >= 2) {
655 fprintf(stdout, "Parsing %s as complete datastore.\n", data_item->filename);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200656 }
Radek Krejci06f8bb92017-08-02 15:36:25 +0200657 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_NO_YANGLIB;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200658 } else if (!strcmp(xml->name, "config")) {
659 if (verbose >= 2) {
660 fprintf(stdout, "Parsing %s as config data.\n", data_item->filename);
661 }
662 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
663 } else if (!strcmp(xml->name, "get-reply")) {
664 if (verbose >= 2) {
665 fprintf(stdout, "Parsing %s as <get> reply data.\n", data_item->filename);
666 }
667 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
668 } else if (!strcmp(xml->name, "get-config-reply")) {
669 if (verbose >= 2) {
670 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", data_item->filename);
671 }
672 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
673 } else if (!strcmp(xml->name, "edit-config")) {
674 if (verbose >= 2) {
675 fprintf(stdout, "Parsing %s as <edit-config> data.\n", data_item->filename);
676 }
677 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
Radek Krejci2d296ce2017-08-02 10:40:39 +0200678 } else if (!strcmp(xml->name, "rpc")) {
679 if (verbose >= 2) {
680 fprintf(stdout, "Parsing %s as <rpc> data.\n", data_item->filename);
681 }
682 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
683 } else if (!strcmp(xml->name, "notification")) {
684 if (verbose >= 2) {
685 fprintf(stdout, "Parsing %s as <notification> data.\n", data_item->filename);
686 }
687 options_parser = (options_parser & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
Radek Krejcifeb903c2016-10-27 17:29:07 +0200688 } else {
689 fprintf(stderr, "yanglint error: invalid top-level element \"%s\" for data type autodetection.\n",
690 xml->name);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200691 goto cleanup;
692 }
693
Radek Krejci06f8bb92017-08-02 15:36:25 +0200694 node = lyd_parse_xml(ctx, &xml->child, options_parser, running);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200695 } else {
Radek Krejci06f8bb92017-08-02 15:36:25 +0200696 node = lyd_parse_path(ctx, data_item->filename, data_item->format, options_parser, running);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200697 }
698 if (ly_errno) {
699 goto cleanup;
700 }
701 if (!root) {
702 root = node;
703 } else if (node) {
704 /* merge results */
705 if (lyd_merge(root, node, LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
706 fprintf(stderr, "yanglint error: merging multiple data trees failed.\n");
707 goto cleanup;
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200708 }
709 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200710 }
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200711
Radek Krejcifeb903c2016-10-27 17:29:07 +0200712 /* print only if data output format specified */
713 if (outformat_d && root) {
714 lyd_print_file(out, root, outformat_d, LYP_WITHSIBLINGS | LYP_FORMAT | options_dflt);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200715 }
716 }
717
718 ret = EXIT_SUCCESS;
719
720cleanup:
Radek Krejci4fd7ef22016-10-20 13:26:41 +0200721 if (out && out != stdout) {
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200722 fclose(out);
723 }
Radek Krejcifeb903c2016-10-27 17:29:07 +0200724 ly_set_free(mods);
Radek Krejci618405d2017-03-25 19:42:03 -0500725 ly_set_free(searchpaths);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200726 for (i = 0; i < featsize; i++) {
727 free(feat[i]);
728 }
729 free(feat);
Radek Krejcifeb903c2016-10-27 17:29:07 +0200730 for (; data; data = data_item) {
731 data_item = data->next;
732 free(data);
733 }
734 lyxml_free(ctx, xml);
735 lyd_free_withsiblings(root);
Radek Krejci6ef5efc2016-10-10 16:38:49 +0200736 ly_ctx_destroy(ctx, NULL);
737
738 return ret;
739}