blob: 361a32dd0c0bea95c1f163b0e72d50bfd1729304 [file] [log] [blame]
Michal Vaskoe0cb2522015-07-01 10:24:53 +02001/**
2 * @file commands.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libyang's yanglint tool commands
Michal Vaskof3e59f12015-06-18 11:53:56 +02005 *
Michal Vaskoe0cb2522015-07-01 10:24:53 +02006 * Copyright (c) 2015 CESNET, z.s.p.o.
Michal Vaskof3e59f12015-06-18 11:53:56 +02007 *
Radek Krejci54f6fb32016-02-24 12:56:39 +01008 * 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
Michal Vasko10e89e72016-03-01 16:00:27 +010011 *
Radek Krejci54f6fb32016-02-24 12:56:39 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vaskof3e59f12015-06-18 11:53:56 +020013 */
Michal Vaskoe0cb2522015-07-01 10:24:53 +020014
Michal Vaskof3e59f12015-06-18 11:53:56 +020015#include <string.h>
16#include <stdio.h>
17#include <errno.h>
Michal Vasko662610a2015-12-07 11:25:45 +010018#include <assert.h>
Michal Vaskof3e59f12015-06-18 11:53:56 +020019#include <sys/types.h>
20#include <sys/stat.h>
Michal Vaskof3e59f12015-06-18 11:53:56 +020021#include <unistd.h>
22#include <getopt.h>
23
Michal Vasko203b4e72015-06-30 15:25:15 +020024#include "commands.h"
Michal Vaskof3e59f12015-06-18 11:53:56 +020025#include "../../src/libyang.h"
Michal Vasko2d162e12015-09-24 14:33:29 +020026#include "../../src/tree_schema.h"
27#include "../../src/tree_data.h"
Michal Vasko520d4732015-07-13 15:53:33 +020028#include "../../src/parser.h"
Michal Vaskoabbdaa02015-10-06 15:47:25 +020029#include "../../src/xpath.h"
Michal Vaskof3e59f12015-06-18 11:53:56 +020030
Michal Vasko203b4e72015-06-30 15:25:15 +020031COMMAND commands[];
Michal Vaskof3e59f12015-06-18 11:53:56 +020032extern int done;
33extern struct ly_ctx *ctx;
Michal Vaskof3e59f12015-06-18 11:53:56 +020034
35void
Michal Vaskof3e59f12015-06-18 11:53:56 +020036cmd_add_help(void)
37{
Michal Vasko370d9102015-08-21 13:06:11 +020038 printf("add <path-to-model> [<other-models> ...]\n");
Michal Vaskof3e59f12015-06-18 11:53:56 +020039}
40
41void
42cmd_print_help(void)
43{
Michal Vasko5d51c0a2016-02-05 14:29:54 +010044 printf("print [-f (yang | yin | tree | info)] [-t <info-target-node>] [-o <output-file>] <model-name>[@<revision>]\n\n");
Michal Vasko3c64ad02016-09-16 14:25:07 +020045 printf("\tinfo-target-node: <absolute-schema-node> | typedef[<absolute-schema-nodeid]/<typedef-name> |\n");
Michal Vasko035f5232015-07-15 12:26:52 +020046 printf("\t | identity/<identity-name> | feature/<feature-name> |\n");
Michal Vaskod04dbea2015-08-06 15:10:19 +020047 printf("\t | grouping/<grouping-name>(<absolute-schema-nodeid>) |\n");
Michal Vasko035f5232015-07-15 12:26:52 +020048 printf("\t | type/<absolute-schema-node-leaf-or-leaflist>\n");
Michal Vaskod04dbea2015-08-06 15:10:19 +020049 printf("\n");
50 printf("\tabsolute-schema-nodeid: ( /(<import-prefix>:)<node-identifier> )+\n");
Michal Vaskof3e59f12015-06-18 11:53:56 +020051}
52
53void
Michal Vasko520d4732015-07-13 15:53:33 +020054cmd_data_help(void)
55{
Michal Vasko78d8a3a2016-09-21 11:25:05 +020056 printf("data [-(-s)trict] [-x OPTION] [-d DEFAULTS] [-o <output-file>] [-f (xml | json)] [-t <additional-tree-file-name>]\n");
57 printf(" <data-file-name> [<JSON-rpc/action-schema-nodeid>]\n");
Radek Krejci9a716122015-12-15 15:15:32 +010058 printf("Accepted OPTIONs:\n");
Radek Krejci4a2aad82016-01-13 15:01:07 +010059 printf("\tauto - resolve data type (one of the following) automatically (as pyang does),\n");
60 printf("\t this option is applicable only in case of XML input data.\n");
Radek Krejci4a49bdf2016-01-12 17:17:01 +010061 printf("\tconfig - LYD_OPT_CONFIG\n");
Radek Krejci9a716122015-12-15 15:15:32 +010062 printf("\tget - LYD_OPT_GET\n");
63 printf("\tgetconfig - LYD_OPT_GETCONFIG\n");
Radek Krejci4a49bdf2016-01-12 17:17:01 +010064 printf("\tedit - LYD_OPT_EDIT\n");
65 printf("\trpc - LYD_OPT_RPC\n");
Michal Vasko26414172016-09-21 09:42:41 +020066 printf("\trpcreply - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
Radek Krejci92ece002016-04-04 15:45:05 +020067 printf("\tnotif - LYD_OPT_NOTIF\n\n");
Michal Vasko104f58c2016-04-11 11:04:13 +020068 printf("Accepted DEFAULTS:\n");
Radek Krejci4bfa6642016-03-23 15:57:45 +010069 printf("\tall - add missing default nodes\n");
Michal Vasko104f58c2016-04-11 11:04:13 +020070 printf("\tall-tagged - add missing default nodes and mark all the default nodes with the attribute.\n");
71 printf("\ttrim - remove all nodes with a default value\n");
Michal Vasko78d8a3a2016-09-21 11:25:05 +020072 printf("\timplicit-tagged - add missing nodes and mark them with the attribute\n\n");
73 printf("Option -t:\n");
74 printf("\tIf RPC/action/notification/RPC reply (for OPTIONs 'rpc', 'rpcreply', and 'notif') includes\n");
75 printf("\tan XPath expression (when/must) that needs access to the configuration data, you can provide\n");
76 printf("\tthem in a file, which will be parsed as 'config'.\n");
Michal Vasko520d4732015-07-13 15:53:33 +020077}
78
79void
Michal Vaskoabbdaa02015-10-06 15:47:25 +020080cmd_xpath_help(void)
81{
Radek Krejcifad28642016-10-31 13:06:57 +010082 printf("xpath [-x OPTION] [-t <additional-tree-file-name>] -e <XPath-expression>\n"
83 " <XML-data-file-name> [<JSON-rpc/action-schema-nodeid>]\n");
84 printf("Accepted OPTIONs:\n");
85 printf("\tauto - resolve data type (one of the following) automatically (as pyang does),\n");
86 printf("\t this option is applicable only in case of XML input data.\n");
87 printf("\tconfig - LYD_OPT_CONFIG\n");
88 printf("\tget - LYD_OPT_GET\n");
89 printf("\tgetconfig - LYD_OPT_GETCONFIG\n");
90 printf("\tedit - LYD_OPT_EDIT\n");
91 printf("\trpc - LYD_OPT_RPC\n");
92 printf("\trpcreply - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
93 printf("\tnotif - LYD_OPT_NOTIF\n\n");
94 printf("Option -t:\n");
95 printf("\tIf RPC/action/notification/RPC reply (for OPTIONs 'rpc', 'rpcreply', and 'notif') includes\n");
96 printf("\tan XPath expression (when/must) that needs access to the configuration data, you can provide\n");
97 printf("\tthem in a file, which will be parsed as 'config'.\n");
Michal Vaskof3e59f12015-06-18 11:53:56 +020098}
99
100void
101cmd_list_help(void)
102{
Radek Krejci83a66b02016-02-25 15:10:31 +0100103 printf("list [-f (xml | json)]\n");
Michal Vaskof3e59f12015-06-18 11:53:56 +0200104}
105
106void
Michal Vasko50cfb782015-07-07 11:37:34 +0200107cmd_feature_help(void)
108{
Michal Vasko87148412015-08-21 14:29:51 +0200109 printf("feature [ -(-e)nable | -(-d)isable (* | <feature-name>[,<feature-name> ...]) ] <model-name>[@<revision>]\n");
Michal Vasko50cfb782015-07-07 11:37:34 +0200110}
111
112void
Michal Vaskof3e59f12015-06-18 11:53:56 +0200113cmd_searchpath_help(void)
114{
Michal Vasko6572fb42015-07-07 09:55:05 +0200115 printf("searchpath <model-dir-path>\n");
Michal Vaskof3e59f12015-06-18 11:53:56 +0200116}
117
118void
119cmd_verb_help(void)
120{
Michal Vaskob93d0142015-08-11 16:08:25 +0200121 printf("verb (error/0 | warning/1 | verbose/2 | debug/3)\n");
Michal Vaskof3e59f12015-06-18 11:53:56 +0200122}
123
Radek Krejci797590b2016-10-10 16:23:04 +0200124LYS_INFORMAT
Radek Krejcidbda8e42016-10-20 13:24:06 +0200125get_schema_format(const char *path)
Radek Krejci797590b2016-10-10 16:23:04 +0200126{
127 char *ptr;
128
129 if ((ptr = strrchr(path, '.')) != NULL) {
130 ++ptr;
131 if (!strcmp(ptr, "yin")) {
132 return LYS_IN_YIN;
133 } else if (!strcmp(ptr, "yang")) {
134 return LYS_IN_YANG;
135 } else {
136 fprintf(stderr, "Input file in an unknown format \"%s\".\n", ptr);
Radek Krejci797590b2016-10-10 16:23:04 +0200137 return LYS_IN_UNKNOWN;
138 }
139 } else {
140 fprintf(stdout, "Input file \"%s\" without extension - unknown format.\n", path);
141 return LYS_IN_UNKNOWN;
142 }
143}
144
Michal Vaskof3e59f12015-06-18 11:53:56 +0200145int
146cmd_add(const char *arg)
147{
Michal Vasko662610a2015-12-07 11:25:45 +0100148 int path_len;
Radek Krejci797590b2016-10-10 16:23:04 +0200149 char *path;
Michal Vasko370d9102015-08-21 13:06:11 +0200150 const char *arg_ptr;
Michal Vasko1e62a092015-12-01 12:27:20 +0100151 const struct lys_module *model;
Radek Krejcia9167ef2015-08-03 11:01:11 +0200152 LYS_INFORMAT format;
Michal Vaskof3e59f12015-06-18 11:53:56 +0200153
Michal Vasko203b4e72015-06-30 15:25:15 +0200154 if (strlen(arg) < 5) {
Michal Vaskof3e59f12015-06-18 11:53:56 +0200155 cmd_add_help();
156 return 1;
157 }
158
Michal Vasko370d9102015-08-21 13:06:11 +0200159 arg_ptr = arg + strlen("add ");
160 while (arg_ptr[0] == ' ') {
161 ++arg_ptr;
162 }
163 if (strchr(arg_ptr, ' ')) {
164 path_len = strchr(arg_ptr, ' ') - arg_ptr;
165 } else {
166 path_len = strlen(arg_ptr);
167 }
Michal Vasko203b4e72015-06-30 15:25:15 +0200168
Michal Vasko370d9102015-08-21 13:06:11 +0200169 path = strndup(arg_ptr, path_len);
170
171 while (path) {
Radek Krejci797590b2016-10-10 16:23:04 +0200172 format = get_schema_format(path);
173 if (format == LYS_IN_UNKNOWN) {
174 free(path);
175 return 1;
Michal Vasko370d9102015-08-21 13:06:11 +0200176 }
177
Michal Vasko662610a2015-12-07 11:25:45 +0100178 model = lys_parse_path(ctx, path, format);
Michal Vasko370d9102015-08-21 13:06:11 +0200179 free(path);
Radek Krejci4041a2d2015-07-02 09:16:42 +0200180
Michal Vasko370d9102015-08-21 13:06:11 +0200181 if (!model) {
182 /* libyang printed the error messages */
183 return 1;
184 }
Radek Krejci4041a2d2015-07-02 09:16:42 +0200185
Michal Vasko370d9102015-08-21 13:06:11 +0200186 /* next model */
187 arg_ptr += path_len;
188 while (arg_ptr[0] == ' ') {
189 ++arg_ptr;
190 }
191 if (strchr(arg_ptr, ' ')) {
192 path_len = strchr(arg_ptr, ' ') - arg_ptr;
193 } else {
194 path_len = strlen(arg_ptr);
195 }
Michal Vaskof3e59f12015-06-18 11:53:56 +0200196
Michal Vasko370d9102015-08-21 13:06:11 +0200197 if (path_len) {
198 path = strndup(arg_ptr, path_len);
199 } else {
200 path = NULL;
201 }
Michal Vaskof3e59f12015-06-18 11:53:56 +0200202 }
203
Michal Vaskof3e59f12015-06-18 11:53:56 +0200204 return 0;
205}
206
207int
208cmd_print(const char *arg)
209{
Michal Vasko462be9a2016-04-05 11:24:08 +0200210 int c, argc, option_index, ret = 1;
Michal Vaskoadfcfa12015-08-12 14:33:44 +0200211 char **argv = NULL, *ptr, *target_node = NULL, *model_name, *revision;
Michal Vasko462be9a2016-04-05 11:24:08 +0200212 const char *out_path = NULL;
213 const struct lys_module *module;
Radek Krejcia9167ef2015-08-03 11:01:11 +0200214 LYS_OUTFORMAT format = LYS_OUT_TREE;
Michal Vaskof3e59f12015-06-18 11:53:56 +0200215 FILE *output = stdout;
216 static struct option long_options[] = {
217 {"help", no_argument, 0, 'h'},
218 {"format", required_argument, 0, 'f'},
219 {"output", required_argument, 0, 'o'},
Michal Vaskodd3b8bc2015-07-07 11:42:28 +0200220 {"target-node", required_argument, 0, 't'},
Michal Vaskof3e59f12015-06-18 11:53:56 +0200221 {NULL, 0, 0, 0}
222 };
223
224 argc = 1;
225 argv = malloc(2*sizeof *argv);
226 *argv = strdup(arg);
227 ptr = strtok(*argv, " ");
228 while ((ptr = strtok(NULL, " "))) {
229 argv = realloc(argv, (argc+2)*sizeof *argv);
230 argv[argc++] = ptr;
231 }
232 argv[argc] = NULL;
233
Michal Vasko50cfb782015-07-07 11:37:34 +0200234 optind = 0;
Michal Vaskof3e59f12015-06-18 11:53:56 +0200235 while (1) {
Michal Vasko1074ce92015-07-03 16:16:31 +0200236 option_index = 0;
Michal Vaskodd3b8bc2015-07-07 11:42:28 +0200237 c = getopt_long(argc, argv, "hf:o:t:", long_options, &option_index);
Michal Vaskof3e59f12015-06-18 11:53:56 +0200238 if (c == -1) {
239 break;
240 }
241
242 switch (c) {
243 case 'h':
244 cmd_print_help();
245 ret = 0;
246 goto cleanup;
247 case 'f':
248 if (!strcmp(optarg, "yang")) {
Radek Krejcia9167ef2015-08-03 11:01:11 +0200249 format = LYS_OUT_YANG;
Michal Vasko5d51c0a2016-02-05 14:29:54 +0100250 } else if (!strcmp(optarg, "yin")) {
251 format = LYS_OUT_YIN;
Michal Vaskof3e59f12015-06-18 11:53:56 +0200252 } else if (!strcmp(optarg, "tree")) {
Radek Krejcia9167ef2015-08-03 11:01:11 +0200253 format = LYS_OUT_TREE;
Michal Vaskodd3b8bc2015-07-07 11:42:28 +0200254 } else if (!strcmp(optarg, "info")) {
Radek Krejcia9167ef2015-08-03 11:01:11 +0200255 format = LYS_OUT_INFO;
Michal Vaskof3e59f12015-06-18 11:53:56 +0200256 } else {
257 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
258 goto cleanup;
259 }
260 break;
261 case 'o':
262 if (out_path) {
263 fprintf(stderr, "Output specified twice.\n");
264 goto cleanup;
265 }
266 out_path = optarg;
267 break;
Michal Vaskodd3b8bc2015-07-07 11:42:28 +0200268 case 't':
269 target_node = optarg;
270 break;
Michal Vaskof3e59f12015-06-18 11:53:56 +0200271 case '?':
272 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
273 goto cleanup;
274 }
275 }
276
277 /* file name */
278 if (optind == argc) {
Michal Vasko462be9a2016-04-05 11:24:08 +0200279 fprintf(stderr, "Missing the module name.\n");
Michal Vaskof3e59f12015-06-18 11:53:56 +0200280 goto cleanup;
281 }
Michal Vaskocf0a41c2015-07-07 11:43:10 +0200282
Michal Vasko462be9a2016-04-05 11:24:08 +0200283 /* module, revision */
Michal Vaskoadfcfa12015-08-12 14:33:44 +0200284 model_name = argv[optind];
285 revision = NULL;
286 if (strchr(model_name, '@')) {
287 revision = strchr(model_name, '@');
288 revision[0] = '\0';
289 ++revision;
290 }
291
Michal Vasko462be9a2016-04-05 11:24:08 +0200292 module = ly_ctx_get_module(ctx, model_name, revision);
293 if (!module) {
294 /* not a module, try to find it as a submodule */
295 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
Michal Vaskocf0a41c2015-07-07 11:43:10 +0200296 }
Michal Vaskof3e59f12015-06-18 11:53:56 +0200297
Michal Vasko462be9a2016-04-05 11:24:08 +0200298 if (!module) {
Michal Vaskoadfcfa12015-08-12 14:33:44 +0200299 if (revision) {
Michal Vasko462be9a2016-04-05 11:24:08 +0200300 fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
Michal Vaskoadfcfa12015-08-12 14:33:44 +0200301 } else {
Michal Vasko462be9a2016-04-05 11:24:08 +0200302 fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
Michal Vaskoadfcfa12015-08-12 14:33:44 +0200303 }
Michal Vaskof3e59f12015-06-18 11:53:56 +0200304 goto cleanup;
305 }
306
307 if (out_path) {
308 output = fopen(out_path, "w");
309 if (!output) {
310 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
311 goto cleanup;
312 }
313 }
314
Michal Vasko462be9a2016-04-05 11:24:08 +0200315 ret = lys_print_file(output, module, format, target_node);
Michal Vaskof3e59f12015-06-18 11:53:56 +0200316
317cleanup:
318 free(*argv);
319 free(argv);
320
Radek Krejci94f05832015-06-19 09:58:53 +0200321 if (output && (output != stdout)) {
Michal Vaskof3e59f12015-06-18 11:53:56 +0200322 fclose(output);
323 }
324
325 return ret;
326}
327
Radek Krejcifad28642016-10-31 13:06:57 +0100328static int
329parse_data(const char *filepath, int options, struct lyd_node *val_tree, const char *act_nodeid,
330 struct lyd_node **result)
331{
332 size_t len;
333 LYD_FORMAT informat = LYD_UNKNOWN;
334 struct lyxml_elem *xml = NULL;
335 const struct lys_node *rpc_act = NULL;
336 struct lyd_node *data = NULL, *root, *next, *iter;
337
338 /* detect input format according to file suffix */
339 len = strlen(filepath);
340 if (len >= 5 && !strcmp(&filepath[len - 4], ".xml")) {
341 informat = LYD_XML;
342 } else if (len >= 6 && !strcmp(&filepath[len - 5], ".json")) {
343 informat = LYD_JSON;
344 } else {
345 fprintf(stderr, "Unable to resolve format of the input file, please add \".xml\" or \".json\" suffix.\n");
346 return EXIT_FAILURE;
347 }
348
349 ly_errno = LY_SUCCESS;
350
351 if ((options & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
352 /* automatically detect data type from the data top level */
353 if (informat != LYD_XML) {
354 fprintf(stderr, "Only XML data can be automatically explored.\n");
355 return EXIT_FAILURE;
356 }
357
358 xml = lyxml_parse_path(ctx, filepath, 0);
359 if (!xml) {
360 fprintf(stderr, "Failed to parse XML data for automatic type detection.\n");
361 return EXIT_FAILURE;
362 }
363
364 /* NOTE: namespace is ignored to simplify usage of this feature */
365
366 if (!strcmp(xml->name, "data")) {
367 fprintf(stdout, "Parsing %s as complete datastore.\n", filepath);
368 options = (options & ~LYD_OPT_TYPEMASK);
369 } else if (!strcmp(xml->name, "config")) {
370 fprintf(stdout, "Parsing %s as config data.\n", filepath);
371 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
372 } else if (!strcmp(xml->name, "get-reply")) {
373 fprintf(stdout, "Parsing %s as <get> reply data.\n", filepath);
374 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
375 } else if (!strcmp(xml->name, "get-config-reply")) {
376 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", filepath);
377 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
378 } else if (!strcmp(xml->name, "edit-config")) {
379 fprintf(stdout, "Parsing %s as <edit-config> data.\n", filepath);
380 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
381 } else if (!strcmp(xml->name, "rpc")) {
382 fprintf(stdout, "Parsing %s as <rpc> data.\n", filepath);
383 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
384 } else if (!strcmp(xml->name, "rpc-reply")) {
385 if (!act_nodeid) {
386 fprintf(stderr, "RPC reply data require additional argument (JSON schema nodeid of the RPC/action).\n");
387 lyxml_free(ctx, xml);
388 return EXIT_FAILURE;
389 }
390 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", filepath);
391 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
392 rpc_act = ly_ctx_get_node(ctx, NULL, act_nodeid);
393 if (!rpc_act || !(rpc_act->nodetype & (LYS_RPC | LYS_ACTION))) {
394 fprintf(stderr, "Invalid JSON schema nodeid.\n");
395 lyxml_free(ctx, xml);
396 return EXIT_FAILURE;
397 }
398 } else if (!strcmp(xml->name, "notification")) {
399 fprintf(stdout, "Parsing %s as <notification> data.\n", filepath);
400 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
401 } else {
402 fprintf(stderr, "Invalid top-level element for automatic data type recognition.\n");
403 lyxml_free(ctx, xml);
404 return EXIT_FAILURE;
405 }
406
407 if (options & LYD_OPT_RPCREPLY) {
408 data = lyd_parse_xml(ctx, &xml->child, options, rpc_act, val_tree);
409 } else if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
410 data = lyd_parse_xml(ctx, &xml->child, options, val_tree);
411 } else {
412 if ((options & LYD_OPT_TYPEMASK) == LYD_OPT_DATA && !(options & LYD_OPT_STRICT )) {
413 /* we have to include status data from ietf-yang-library which is part of the context,
414 * so we have to postpone validation after merging input data with ly_ctx_info() */
415 options |= LYD_OPT_TRUSTED;
416 }
417 data = lyd_parse_xml(ctx, &xml->child, options);
418 }
419 lyxml_free(ctx, xml);
420 } else {
421 if (options & LYD_OPT_RPCREPLY) {
422 if (act_nodeid) {
423 fprintf(stderr, "RPC reply data require additional argument (schema nodeid of the RPC/action).\n");
424 return EXIT_FAILURE;
425 }
426 rpc_act = ly_ctx_get_node(ctx, NULL, act_nodeid);
427 if (!rpc_act || !(rpc_act->nodetype & (LYS_RPC | LYS_ACTION))) {
428 fprintf(stderr, "Invalid JSON schema nodeid.\n");
429 return EXIT_FAILURE;
430 }
431 data = lyd_parse_path(ctx, filepath, informat, options, rpc_act, val_tree);
432 } else if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
433 data = lyd_parse_path(ctx, filepath, informat, options, val_tree);
434 } else {
435 if ((options & LYD_OPT_TYPEMASK) == LYD_OPT_DATA && !(options & LYD_OPT_STRICT )) {
436 /* we have to include status data from ietf-yang-library which is part of the context,
437 * so we have to postpone validation after merging input data with ly_ctx_info() */
438 options |= LYD_OPT_TRUSTED;
439 }
440 data = lyd_parse_path(ctx, filepath, informat, options);
441 }
442 }
443 if (ly_errno) {
444 fprintf(stderr, "Failed to parse data.\n");
445 lyd_free_withsiblings(data);
446 return EXIT_FAILURE;
447 }
448
449 if (options & LYD_OPT_TRUSTED) {
450 /* postponed validation in case of LYD_OPT_DATA */
451 /* remove the trusted flag */
452 options &= ~LYD_OPT_TRUSTED;
453
454 /* merge with ietf-yang-library */
455 root = ly_ctx_info(ctx);
456 if (lyd_merge(root, data, LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
457 fprintf(stderr, "Merging input data with ietf-yang-library failed.\n");
458 lyd_free_withsiblings(root);
459 lyd_free_withsiblings(data);
460 return EXIT_FAILURE;
461 }
462 data = root;
463
464 /* invalidate all data - do not believe to the source */
465 LY_TREE_FOR(data, root) {
466 LY_TREE_DFS_BEGIN(root, next, iter) {
467 iter->validity = LYD_VAL_NOT;
468 LY_TREE_DFS_END(root, next, iter)
469 }
470 }
471
472 /* validate the result */
473 if (lyd_validate(&data, options, NULL)) {
474 fprintf(stderr, "Failed to parse data.\n");
475 lyd_free_withsiblings(data);
476 return EXIT_FAILURE;
477 }
478 }
479
480 *result = data;
481 return EXIT_SUCCESS;
482}
483
Radek Krejciba04f3e2015-11-10 19:16:01 +0100484int
485cmd_data(const char *arg)
Michal Vasko520d4732015-07-13 15:53:33 +0200486{
Michal Vasko662610a2015-12-07 11:25:45 +0100487 int c, argc, option_index, ret = 1;
Radek Krejci46180b52016-08-31 16:01:32 +0200488 int options = 0, printopt = 0;
Michal Vasko662610a2015-12-07 11:25:45 +0100489 char **argv = NULL, *ptr;
Michal Vasko520d4732015-07-13 15:53:33 +0200490 const char *out_path = NULL;
Radek Krejcifad28642016-10-31 13:06:57 +0100491 struct lyd_node *data = NULL, *val_tree = NULL;
492 LYD_FORMAT outformat = LYD_UNKNOWN;
Michal Vasko520d4732015-07-13 15:53:33 +0200493 FILE *output = stdout;
494 static struct option long_options[] = {
Radek Krejci4bfa6642016-03-23 15:57:45 +0100495 {"defaults", required_argument, 0, 'd'},
Michal Vasko520d4732015-07-13 15:53:33 +0200496 {"help", no_argument, 0, 'h'},
497 {"format", required_argument, 0, 'f'},
Radek Krejciba04f3e2015-11-10 19:16:01 +0100498 {"option", required_argument, 0, 'x'},
Michal Vasko520d4732015-07-13 15:53:33 +0200499 {"output", required_argument, 0, 'o'},
Radek Krejcib9120952015-08-12 10:03:51 +0200500 {"strict", no_argument, 0, 's'},
Michal Vasko78d8a3a2016-09-21 11:25:05 +0200501 {"validation-tree", required_argument, 0, 't'},
Michal Vasko520d4732015-07-13 15:53:33 +0200502 {NULL, 0, 0, 0}
503 };
504
505 argc = 1;
506 argv = malloc(2*sizeof *argv);
507 *argv = strdup(arg);
508 ptr = strtok(*argv, " ");
509 while ((ptr = strtok(NULL, " "))) {
510 argv = realloc(argv, (argc+2)*sizeof *argv);
511 argv[argc++] = ptr;
512 }
513 argv[argc] = NULL;
514
515 optind = 0;
516 while (1) {
517 option_index = 0;
Michal Vasko78d8a3a2016-09-21 11:25:05 +0200518 c = getopt_long(argc, argv, "d:hf:o:sx:t:", long_options, &option_index);
Michal Vasko520d4732015-07-13 15:53:33 +0200519 if (c == -1) {
520 break;
521 }
522
523 switch (c) {
Radek Krejci4bfa6642016-03-23 15:57:45 +0100524 case 'd':
525 if (!strcmp(optarg, "all")) {
Radek Krejci46180b52016-08-31 16:01:32 +0200526 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL;
Radek Krejci4bfa6642016-03-23 15:57:45 +0100527 } else if (!strcmp(optarg, "all-tagged")) {
Radek Krejci46180b52016-08-31 16:01:32 +0200528 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
Radek Krejci4bfa6642016-03-23 15:57:45 +0100529 } else if (!strcmp(optarg, "trim")) {
Radek Krejci46180b52016-08-31 16:01:32 +0200530 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_TRIM;
Radek Krejci4bfa6642016-03-23 15:57:45 +0100531 } else if (!strcmp(optarg, "implicit-tagged")) {
Radek Krejci46180b52016-08-31 16:01:32 +0200532 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
Radek Krejci4bfa6642016-03-23 15:57:45 +0100533 }
534 break;
Michal Vasko520d4732015-07-13 15:53:33 +0200535 case 'h':
536 cmd_data_help();
537 ret = 0;
538 goto cleanup;
539 case 'f':
540 if (!strcmp(optarg, "xml")) {
Michal Vasko95068c42016-03-24 14:58:11 +0100541 outformat = LYD_XML;
Michal Vasko520d4732015-07-13 15:53:33 +0200542 } else if (!strcmp(optarg, "json")) {
Radek Krejci5449d472015-10-26 14:35:56 +0100543 outformat = LYD_JSON;
Michal Vasko520d4732015-07-13 15:53:33 +0200544 } else {
545 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
546 goto cleanup;
547 }
548 break;
549 case 'o':
550 if (out_path) {
551 fprintf(stderr, "Output specified twice.\n");
552 goto cleanup;
553 }
554 out_path = optarg;
555 break;
Radek Krejcib9120952015-08-12 10:03:51 +0200556 case 's':
557 options |= LYD_OPT_STRICT;
Radek Krejci4a49bdf2016-01-12 17:17:01 +0100558 options |= LYD_OPT_OBSOLETE;
Radek Krejcib9120952015-08-12 10:03:51 +0200559 break;
Radek Krejciba04f3e2015-11-10 19:16:01 +0100560 case 'x':
Radek Krejci4a2aad82016-01-13 15:01:07 +0100561 if (!strcmp(optarg, "auto")) {
562 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
563 } else if (!strcmp(optarg, "config")) {
564 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
Radek Krejciba04f3e2015-11-10 19:16:01 +0100565 } else if (!strcmp(optarg, "get")) {
Radek Krejci4a2aad82016-01-13 15:01:07 +0100566 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
Radek Krejciba04f3e2015-11-10 19:16:01 +0100567 } else if (!strcmp(optarg, "getconfig")) {
Radek Krejci4a2aad82016-01-13 15:01:07 +0100568 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
Radek Krejci4a49bdf2016-01-12 17:17:01 +0100569 } else if (!strcmp(optarg, "edit")) {
Radek Krejci4a2aad82016-01-13 15:01:07 +0100570 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
Radek Krejci4a49bdf2016-01-12 17:17:01 +0100571 } else if (!strcmp(optarg, "rpc")) {
Radek Krejci4a2aad82016-01-13 15:01:07 +0100572 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
Radek Krejci4a49bdf2016-01-12 17:17:01 +0100573 } else if (!strcmp(optarg, "rpcreply")) {
Radek Krejci4a2aad82016-01-13 15:01:07 +0100574 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
Radek Krejci4a49bdf2016-01-12 17:17:01 +0100575 } else if (!strcmp(optarg, "notif")) {
Radek Krejci4a2aad82016-01-13 15:01:07 +0100576 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
Radek Krejci83b5ae52016-03-11 11:17:26 +0100577 } else {
578 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
579 cmd_data_help();
580 goto cleanup;
Radek Krejciba04f3e2015-11-10 19:16:01 +0100581 }
582 break;
Michal Vasko78d8a3a2016-09-21 11:25:05 +0200583 case 't':
584 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_CONFIG);
585 if (!val_tree) {
586 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
587 goto cleanup;
588 }
589 break;
Michal Vasko520d4732015-07-13 15:53:33 +0200590 case '?':
591 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
592 goto cleanup;
593 }
594 }
595
596 /* file name */
597 if (optind == argc) {
598 fprintf(stderr, "Missing the data file name.\n");
599 goto cleanup;
600 }
601
Radek Krejcifad28642016-10-31 13:06:57 +0100602 if (parse_data(argv[optind], options, val_tree, argv[optind + 1], &data)) {
Michal Vasko520d4732015-07-13 15:53:33 +0200603 goto cleanup;
604 }
605
606 if (out_path) {
607 output = fopen(out_path, "w");
608 if (!output) {
609 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
610 goto cleanup;
611 }
Michal Vasko520d4732015-07-13 15:53:33 +0200612 }
613
Radek Krejci5449d472015-10-26 14:35:56 +0100614 if (outformat != LYD_UNKNOWN) {
Michal Vasko26414172016-09-21 09:42:41 +0200615 if (options & LYD_OPT_RPCREPLY) {
616 lyd_print_file(output, data->child, outformat, LYP_WITHSIBLINGS | LYP_FORMAT | printopt);
617 } else {
618 lyd_print_file(output, data, outformat, LYP_WITHSIBLINGS | LYP_FORMAT | printopt);
619 }
Michal Vasko520d4732015-07-13 15:53:33 +0200620 }
621
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200622 ret = 0;
623
Michal Vasko520d4732015-07-13 15:53:33 +0200624cleanup:
625 free(*argv);
626 free(argv);
627
628 if (output && (output != stdout)) {
629 fclose(output);
630 }
631
Michal Vasko78d8a3a2016-09-21 11:25:05 +0200632 lyd_free_withsiblings(val_tree);
Radek Krejci4a2aad82016-01-13 15:01:07 +0100633 lyd_free_withsiblings(data);
Michal Vasko520d4732015-07-13 15:53:33 +0200634
635 return ret;
636}
637
638int
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200639cmd_xpath(const char *arg)
640{
Michal Vaskoa8c77c22016-02-04 12:08:40 +0100641 int c, argc, option_index, ret = 1, long_str;
642 char **argv = NULL, *ptr, *expr = NULL;
643 unsigned int i, j;
Radek Krejcifad28642016-10-31 13:06:57 +0100644 int options = 0;
645 struct lyd_node *data = NULL, *node, *val_tree = NULL;
Michal Vaskof29903d2016-04-18 13:13:10 +0200646 struct lyd_node_leaf_list *key;
647 struct ly_set *set;
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200648 static struct option long_options[] = {
649 {"help", no_argument, 0, 'h'},
650 {"expr", required_argument, 0, 'e'},
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200651 {NULL, 0, 0, 0}
652 };
653
654 long_str = 0;
655 argc = 1;
656 argv = malloc(2 * sizeof *argv);
657 *argv = strdup(arg);
658 ptr = strtok(*argv, " ");
659 while ((ptr = strtok(NULL, " "))) {
660 if (long_str) {
661 ptr[-1] = ' ';
662 if (ptr[strlen(ptr) - 1] == long_str) {
663 long_str = 0;
664 ptr[strlen(ptr) - 1] = '\0';
665 }
666 } else {
667 argv = realloc(argv, (argc + 2) * sizeof *argv);
668 argv[argc] = ptr;
669 if (ptr[0] == '"') {
670 long_str = '"';
671 ++argv[argc];
672 }
673 if (ptr[0] == '\'') {
674 long_str = '\'';
675 ++argv[argc];
676 }
677 if (ptr[strlen(ptr) - 1] == long_str) {
678 long_str = 0;
679 ptr[strlen(ptr) - 1] = '\0';
680 }
681 ++argc;
682 }
683 }
684 argv[argc] = NULL;
685
686 optind = 0;
687 while (1) {
688 option_index = 0;
Radek Krejcifad28642016-10-31 13:06:57 +0100689 c = getopt_long(argc, argv, "he:t:x:", long_options, &option_index);
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200690 if (c == -1) {
691 break;
692 }
693
694 switch (c) {
695 case 'h':
696 cmd_xpath_help();
697 ret = 0;
698 goto cleanup;
699 case 'e':
700 expr = optarg;
701 break;
Radek Krejcifad28642016-10-31 13:06:57 +0100702 case 'x':
703 if (!strcmp(optarg, "auto")) {
704 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
705 } else if (!strcmp(optarg, "config")) {
706 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
707 } else if (!strcmp(optarg, "get")) {
708 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
709 } else if (!strcmp(optarg, "getconfig")) {
710 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
711 } else if (!strcmp(optarg, "edit")) {
712 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
713 } else if (!strcmp(optarg, "rpc")) {
714 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
715 } else if (!strcmp(optarg, "rpcreply")) {
716 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
717 } else if (!strcmp(optarg, "notif")) {
718 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
719 } else {
720 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
721 cmd_data_help();
722 goto cleanup;
723 }
724 break;
725 case 't':
726 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_CONFIG);
727 if (!val_tree) {
728 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
729 goto cleanup;
730 }
731 break;
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200732 case '?':
733 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
734 goto cleanup;
735 }
736 }
737
738 if (optind == argc) {
739 fprintf(stderr, "Missing the file with data.\n");
740 goto cleanup;
741 }
742
743 if (!expr) {
744 fprintf(stderr, "Missing the XPath expression.\n");
745 goto cleanup;
746 }
747
Radek Krejcifad28642016-10-31 13:06:57 +0100748 if (parse_data(argv[optind], options, val_tree, argv[optind + 1], &data)) {
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200749 goto cleanup;
750 }
751
Michal Vaskof06fb5b2016-09-08 10:05:56 +0200752 if (!(set = lyd_find_xpath(data, expr))) {
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200753 goto cleanup;
754 }
755
Michal Vaskoa8c77c22016-02-04 12:08:40 +0100756 /* print result */
757 printf("Result:\n");
758 if (!set->number) {
759 printf("\tEmpty\n");
760 } else {
761 for (i = 0; i < set->number; ++i) {
Radek Krejci8f08df12016-03-21 11:11:30 +0100762 node = set->set.d[i];
Michal Vaskoa8c77c22016-02-04 12:08:40 +0100763 switch (node->schema->nodetype) {
764 case LYS_CONTAINER:
765 printf("\tContainer ");
766 break;
767 case LYS_LEAF:
768 printf("\tLeaf ");
769 break;
770 case LYS_LEAFLIST:
771 printf("\tLeaflist ");
772 break;
773 case LYS_LIST:
Michal Vaskodbb86322016-02-04 13:53:38 +0100774 printf("\tList ");
Michal Vaskoa8c77c22016-02-04 12:08:40 +0100775 break;
776 case LYS_ANYXML:
777 printf("\tAnyxml ");
778 break;
Radek Krejcibf2abff2016-08-23 15:51:52 +0200779 case LYS_ANYDATA:
780 printf("\tAnydata ");
781 break;
Michal Vaskoa8c77c22016-02-04 12:08:40 +0100782 default:
783 printf("\tUnknown ");
784 break;
785 }
786 printf("\"%s\"", node->schema->name);
787 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
788 printf(" (val: %s)", ((struct lyd_node_leaf_list *)node)->value_str);
789 } else if (node->schema->nodetype == LYS_LIST) {
Michal Vaskof29903d2016-04-18 13:13:10 +0200790 key = (struct lyd_node_leaf_list *)node->child;
791 printf(" (");
792 for (j = 0; j < ((struct lys_node_list *)node->schema)->keys_size; ++j) {
793 if (j) {
794 printf(" ");
Michal Vaskoa8c77c22016-02-04 12:08:40 +0100795 }
Michal Vaskof29903d2016-04-18 13:13:10 +0200796 printf("\"%s\": %s", key->schema->name, key->value_str);
797 key = (struct lyd_node_leaf_list *)key->next;
Michal Vaskoa8c77c22016-02-04 12:08:40 +0100798 }
Michal Vaskof29903d2016-04-18 13:13:10 +0200799 printf(")");
Michal Vaskoa8c77c22016-02-04 12:08:40 +0100800 }
801 printf("\n");
802 }
803 }
804 printf("\n");
805
Michal Vasko2b67bf72016-05-05 17:49:36 +0200806 ly_set_free(set);
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200807 ret = 0;
808
809cleanup:
810 free(*argv);
811 free(argv);
812
Radek Krejci4a2aad82016-01-13 15:01:07 +0100813 lyd_free_withsiblings(data);
Michal Vaskoabbdaa02015-10-06 15:47:25 +0200814
815 return ret;
816}
817
818int
Radek Krejci83a66b02016-02-25 15:10:31 +0100819cmd_list(const char *arg)
Michal Vaskof3e59f12015-06-18 11:53:56 +0200820{
Radek Krejcifc3692c2016-02-25 15:36:13 +0100821 struct lyd_node *ylib = NULL, *module, *submodule, *node;
Radek Krejci6e05cea2015-12-10 16:34:37 +0100822 int has_modules = 0, flag;
Radek Krejci83a66b02016-02-25 15:10:31 +0100823 char **argv = NULL, *ptr;
824 int c, argc, option_index;
825 LYD_FORMAT outformat = LYD_UNKNOWN;
826 static struct option long_options[] = {
827 {"help", no_argument, 0, 'h'},
828 {"format", required_argument, 0, 'f'},
829 {NULL, 0, 0, 0}
830 };
831
832 argc = 1;
833 argv = malloc(2*sizeof *argv);
834 *argv = strdup(arg);
835 ptr = strtok(*argv, " ");
836 while ((ptr = strtok(NULL, " "))) {
837 argv = realloc(argv, (argc+2)*sizeof *argv);
838 argv[argc++] = ptr;
839 }
840 argv[argc] = NULL;
841
842 optind = 0;
843 while (1) {
844 option_index = 0;
845 c = getopt_long(argc, argv, "hf:", long_options, &option_index);
846 if (c == -1) {
847 break;
848 }
849
850 switch (c) {
851 case 'h':
852 cmd_data_help();
853 free(*argv);
854 free(argv);
855 return 0;
856 case 'f':
857 if (!strcmp(optarg, "xml")) {
Michal Vasko95068c42016-03-24 14:58:11 +0100858 outformat = LYD_XML;
Radek Krejci83a66b02016-02-25 15:10:31 +0100859 } else if (!strcmp(optarg, "json")) {
860 outformat = LYD_JSON;
861 } else {
862 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
Radek Krejcifc3692c2016-02-25 15:36:13 +0100863 goto error;
Radek Krejci83a66b02016-02-25 15:10:31 +0100864 }
865 break;
866 case '?':
Radek Krejcifc3692c2016-02-25 15:36:13 +0100867 /* getopt_long() prints message */
868 goto error;
Radek Krejci83a66b02016-02-25 15:10:31 +0100869 }
870 }
Radek Krejcifc3692c2016-02-25 15:36:13 +0100871 if (optind != argc) {
872 fprintf(stderr, "Unknown parameter \"%s\"\n", argv[optind]);
873error:
874 free(*argv);
875 free(argv);
876 return 1;
877 }
Radek Krejci83a66b02016-02-25 15:10:31 +0100878 free(*argv);
879 free(argv);
880
Radek Krejci7ab25152015-08-07 14:48:45 +0200881 ylib = ly_ctx_info(ctx);
Michal Vasko3e671b52015-10-23 16:23:15 +0200882 if (!ylib) {
Michal Vasko7c7023f2015-12-11 11:59:54 +0100883 fprintf(stderr, "Getting context info (ietf-yang-library data) failed.\n");
Michal Vasko3e671b52015-10-23 16:23:15 +0200884 return 1;
885 }
Michal Vaskof3e59f12015-06-18 11:53:56 +0200886
Radek Krejci83a66b02016-02-25 15:10:31 +0100887 if (outformat != LYD_UNKNOWN) {
Michal Vasko95068c42016-03-24 14:58:11 +0100888 lyd_print_file(stdout, ylib, outformat, LYP_WITHSIBLINGS | LYP_FORMAT);
Radek Krejcidd1c4172016-02-25 15:17:45 +0100889 lyd_free(ylib);
Radek Krejci83a66b02016-02-25 15:10:31 +0100890 return 0;
891 }
892
Michal Vaskoea31f192015-08-03 15:21:56 +0200893 LY_TREE_FOR(ylib->child, node) {
894 if (!strcmp(node->schema->name, "module-set-id")) {
Michal Vasko4c183312015-09-25 10:41:47 +0200895 printf("List of the loaded models (mod-set-id %s):\n", ((struct lyd_node_leaf_list *)node)->value_str);
Michal Vaskoea31f192015-08-03 15:21:56 +0200896 break;
Michal Vaskofa8c8282015-07-03 15:14:59 +0200897 }
Michal Vaskoe0cb2522015-07-01 10:24:53 +0200898 }
Michal Vaskoea31f192015-08-03 15:21:56 +0200899 assert(node);
Michal Vaskoe0cb2522015-07-01 10:24:53 +0200900
Michal Vaskoea31f192015-08-03 15:21:56 +0200901 LY_TREE_FOR(ylib->child, module) {
902 if (!strcmp(module->schema->name, "module")) {
903 has_modules = 1;
904
905 /* module print */
906 LY_TREE_FOR(module->child, node) {
907 if (!strcmp(node->schema->name, "name")) {
Michal Vasko4c183312015-09-25 10:41:47 +0200908 printf("\t%s", ((struct lyd_node_leaf_list *)node)->value_str);
Michal Vaskoea31f192015-08-03 15:21:56 +0200909 } else if (!strcmp(node->schema->name, "revision")) {
Radek Krejci6e05cea2015-12-10 16:34:37 +0100910 if (((struct lyd_node_leaf_list *)node)->value_str[0] != '\0') {
911 printf("@%s", ((struct lyd_node_leaf_list *)node)->value_str);
Michal Vaskoea31f192015-08-03 15:21:56 +0200912 }
913 }
914 }
915
916 /* submodules print */
917 LY_TREE_FOR(module->child, submodule) {
Radek Krejcid9723912016-09-16 17:06:06 +0200918 if (!strcmp(submodule->schema->name, "submodule")) {
Radek Krejci6e05cea2015-12-10 16:34:37 +0100919 printf(" (");
920 flag = 0;
Radek Krejcid9723912016-09-16 17:06:06 +0200921 LY_TREE_FOR(submodule, submodule) {
Michal Vaskoea31f192015-08-03 15:21:56 +0200922 if (!strcmp(submodule->schema->name, "submodule")) {
923 LY_TREE_FOR(submodule->child, node) {
924 if (!strcmp(node->schema->name, "name")) {
Radek Krejci6e05cea2015-12-10 16:34:37 +0100925 printf("%s%s", flag ? "," : "", ((struct lyd_node_leaf_list *)node)->value_str);
Michal Vaskoea31f192015-08-03 15:21:56 +0200926 } else if (!strcmp(node->schema->name, "revision")) {
Radek Krejci6e05cea2015-12-10 16:34:37 +0100927 if (((struct lyd_node_leaf_list *)node)->value_str[0] != '\0') {
928 printf("@%s", ((struct lyd_node_leaf_list *)node)->value_str);
Michal Vaskoea31f192015-08-03 15:21:56 +0200929 }
930 }
931 }
Radek Krejci6e05cea2015-12-10 16:34:37 +0100932 flag++;
Michal Vaskoea31f192015-08-03 15:21:56 +0200933 }
934 }
Radek Krejci6e05cea2015-12-10 16:34:37 +0100935 printf(")");
Michal Vaskoea31f192015-08-03 15:21:56 +0200936 break;
937 }
938 }
Radek Krejci6e05cea2015-12-10 16:34:37 +0100939 printf("\n");
Michal Vaskoea31f192015-08-03 15:21:56 +0200940 }
941 }
942
943 if (!has_modules) {
Michal Vaskoe0cb2522015-07-01 10:24:53 +0200944 printf("\t(none)\n");
945 }
Michal Vaskof3e59f12015-06-18 11:53:56 +0200946
Michal Vaskoea31f192015-08-03 15:21:56 +0200947 lyd_free(ylib);
Michal Vaskof3e59f12015-06-18 11:53:56 +0200948 return 0;
949}
950
951int
Michal Vasko50cfb782015-07-07 11:37:34 +0200952cmd_feature(const char *arg)
953{
Michal Vasko87148412015-08-21 14:29:51 +0200954 int c, i, argc, option_index, ret = 1, task = 0;
Michal Vaskob011e6b2015-08-06 09:57:46 +0200955 unsigned int max_len;
Michal Vasko87148412015-08-21 14:29:51 +0200956 char **argv = NULL, *ptr, *model_name, *revision, *feat_names = NULL;
957 const char **names;
Radek Krejcie98bb4b2015-07-30 14:21:41 +0200958 uint8_t *states;
Michal Vasko462be9a2016-04-05 11:24:08 +0200959 const struct lys_module *module;
Michal Vasko50cfb782015-07-07 11:37:34 +0200960 static struct option long_options[] = {
961 {"help", no_argument, 0, 'h'},
Michal Vasko50cfb782015-07-07 11:37:34 +0200962 {"enable", required_argument, 0, 'e'},
963 {"disable", required_argument, 0, 'd'},
964 {NULL, 0, 0, 0}
965 };
966
967 argc = 1;
968 argv = malloc(2*sizeof *argv);
969 *argv = strdup(arg);
970 ptr = strtok(*argv, " ");
971 while ((ptr = strtok(NULL, " "))) {
972 argv = realloc(argv, (argc+2)*sizeof *argv);
973 argv[argc++] = ptr;
974 }
975 argv[argc] = NULL;
976
977 optind = 0;
978 while (1) {
979 option_index = 0;
Michal Vasko87148412015-08-21 14:29:51 +0200980 c = getopt_long(argc, argv, "he:d:", long_options, &option_index);
Michal Vasko50cfb782015-07-07 11:37:34 +0200981 if (c == -1) {
982 break;
983 }
984
985 switch (c) {
986 case 'h':
987 cmd_feature_help();
988 ret = 0;
989 goto cleanup;
Michal Vasko50cfb782015-07-07 11:37:34 +0200990 case 'e':
Michal Vasko87148412015-08-21 14:29:51 +0200991 if (task) {
992 fprintf(stderr, "Only one of enable or disable can be specified.\n");
Michal Vasko50cfb782015-07-07 11:37:34 +0200993 goto cleanup;
994 }
995 task = 1;
Michal Vasko87148412015-08-21 14:29:51 +0200996 feat_names = optarg;
Michal Vasko50cfb782015-07-07 11:37:34 +0200997 break;
998 case 'd':
Michal Vasko87148412015-08-21 14:29:51 +0200999 if (task) {
1000 fprintf(stderr, "Only one of enable, or disable can be specified.\n");
Michal Vasko50cfb782015-07-07 11:37:34 +02001001 goto cleanup;
1002 }
1003 task = 2;
Michal Vasko87148412015-08-21 14:29:51 +02001004 feat_names = optarg;
Michal Vasko50cfb782015-07-07 11:37:34 +02001005 break;
1006 case '?':
1007 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
1008 goto cleanup;
1009 }
1010 }
1011
Michal Vasko462be9a2016-04-05 11:24:08 +02001012 /* module name */
Michal Vasko50cfb782015-07-07 11:37:34 +02001013 if (optind == argc) {
Michal Vasko462be9a2016-04-05 11:24:08 +02001014 fprintf(stderr, "Missing the module name.\n");
Michal Vasko50cfb782015-07-07 11:37:34 +02001015 goto cleanup;
1016 }
Michal Vasko4b484272015-08-12 14:41:25 +02001017
1018 revision = NULL;
1019 model_name = argv[optind];
1020 if (strchr(model_name, '@')) {
1021 revision = strchr(model_name, '@');
1022 revision[0] = '\0';
1023 ++revision;
1024 }
1025
Michal Vasko462be9a2016-04-05 11:24:08 +02001026 module = ly_ctx_get_module(ctx, model_name, revision);
1027 if (!module) {
1028 /* not a module, try to find it as a submodule */
1029 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
Michal Vasko50cfb782015-07-07 11:37:34 +02001030 }
Michal Vasko462be9a2016-04-05 11:24:08 +02001031
1032 if (module == NULL) {
Michal Vasko4b484272015-08-12 14:41:25 +02001033 if (revision) {
Michal Vasko462be9a2016-04-05 11:24:08 +02001034 fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
Michal Vasko4b484272015-08-12 14:41:25 +02001035 } else {
Michal Vasko462be9a2016-04-05 11:24:08 +02001036 fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
Michal Vasko4b484272015-08-12 14:41:25 +02001037 }
Michal Vasko50cfb782015-07-07 11:37:34 +02001038 goto cleanup;
1039 }
1040
Michal Vasko87148412015-08-21 14:29:51 +02001041 if (!task) {
Michal Vasko462be9a2016-04-05 11:24:08 +02001042 printf("%s features:\n", module->name);
Michal Vasko50cfb782015-07-07 11:37:34 +02001043
Michal Vasko462be9a2016-04-05 11:24:08 +02001044 names = lys_features_list(module, &states);
Michal Vaskob011e6b2015-08-06 09:57:46 +02001045
1046 /* get the max len */
1047 max_len = 0;
Michal Vasko50cfb782015-07-07 11:37:34 +02001048 for (i = 0; names[i]; ++i) {
Michal Vaskob011e6b2015-08-06 09:57:46 +02001049 if (strlen(names[i]) > max_len) {
1050 max_len = strlen(names[i]);
1051 }
1052 }
1053 for (i = 0; names[i]; ++i) {
1054 printf("\t%-*s (%s)\n", max_len, names[i], states[i] ? "on" : "off");
Michal Vasko50cfb782015-07-07 11:37:34 +02001055 }
1056 free(names);
Radek Krejcie98bb4b2015-07-30 14:21:41 +02001057 free(states);
Michal Vasko50cfb782015-07-07 11:37:34 +02001058 if (!i) {
1059 printf("\t(none)\n");
1060 }
Michal Vasko87148412015-08-21 14:29:51 +02001061 } else {
1062 feat_names = strtok(feat_names, ",");
1063 while (feat_names) {
Michal Vasko462be9a2016-04-05 11:24:08 +02001064 if (((task == 1) && lys_features_enable(module, feat_names))
1065 || ((task == 2) && lys_features_disable(module, feat_names))) {
Michal Vasko87148412015-08-21 14:29:51 +02001066 fprintf(stderr, "Feature \"%s\" not found.\n", feat_names);
1067 ret = 1;
1068 }
1069 feat_names = strtok(NULL, ",");
Michal Vasko50cfb782015-07-07 11:37:34 +02001070 }
1071 }
1072
1073cleanup:
1074 free(*argv);
1075 free(argv);
1076
1077 return ret;
1078}
1079
1080int
Michal Vaskof3e59f12015-06-18 11:53:56 +02001081cmd_searchpath(const char *arg)
1082{
1083 const char *path;
1084 struct stat st;
1085
1086 if (strchr(arg, ' ') == NULL) {
1087 fprintf(stderr, "Missing the search path.\n");
1088 return 1;
1089 }
1090 path = strchr(arg, ' ')+1;
1091
1092 if (!strcmp(path, "-h") || !strcmp(path, "--help")) {
1093 cmd_searchpath_help();
1094 return 0;
1095 }
1096
1097 if (stat(path, &st) == -1) {
1098 fprintf(stderr, "Failed to stat the search path (%s).\n", strerror(errno));
1099 return 1;
1100 }
1101 if (!S_ISDIR(st.st_mode)) {
1102 fprintf(stderr, "\"%s\" is not a directory.\n", path);
1103 return 1;
1104 }
1105
Radek Krejci0cb47842015-12-09 15:24:32 +01001106 ly_ctx_set_searchdir(ctx, path);
Michal Vaskof3e59f12015-06-18 11:53:56 +02001107
Michal Vasko6572fb42015-07-07 09:55:05 +02001108 return 0;
1109}
Michal Vaskof3e59f12015-06-18 11:53:56 +02001110
Michal Vasko50cfb782015-07-07 11:37:34 +02001111int
1112cmd_clear(const char *UNUSED(arg))
1113{
Radek Krejcifa0b5e02016-02-04 13:57:03 +01001114 ly_ctx_destroy(ctx, NULL);
Radek Krejci0cb47842015-12-09 15:24:32 +01001115 ctx = ly_ctx_new(NULL);
Michal Vasko5f998eb2015-08-21 14:05:58 +02001116 if (!ctx) {
1117 fprintf(stderr, "Failed to create context.\n");
1118 return 1;
1119 }
Michal Vaskof3e59f12015-06-18 11:53:56 +02001120 return 0;
1121}
1122
1123int
1124cmd_verb(const char *arg)
1125{
1126 const char *verb;
Radek Krejci7408bf12015-07-20 18:15:51 +02001127 if (strlen(arg) < 5) {
1128 cmd_verb_help();
1129 return 1;
1130 }
Michal Vaskof3e59f12015-06-18 11:53:56 +02001131
1132 verb = arg + 5;
Michal Vaskob93d0142015-08-11 16:08:25 +02001133 if (!strcmp(verb, "error") || !strcmp(verb, "0")) {
Radek Krejci2abfdfe2016-07-19 11:05:06 +02001134 ly_verb(LY_LLERR);
Michal Vaskob93d0142015-08-11 16:08:25 +02001135 } else if (!strcmp(verb, "warning") || !strcmp(verb, "1")) {
Radek Krejci2abfdfe2016-07-19 11:05:06 +02001136 ly_verb(LY_LLWRN);
Michal Vaskob93d0142015-08-11 16:08:25 +02001137 } else if (!strcmp(verb, "verbose") || !strcmp(verb, "2")) {
Radek Krejci2abfdfe2016-07-19 11:05:06 +02001138 ly_verb(LY_LLVRB);
Michal Vaskob93d0142015-08-11 16:08:25 +02001139 } else if (!strcmp(verb, "debug") || !strcmp(verb, "3")) {
Radek Krejci2abfdfe2016-07-19 11:05:06 +02001140 ly_verb(LY_LLDBG);
Michal Vaskof3e59f12015-06-18 11:53:56 +02001141 } else {
Radek Krejci7408bf12015-07-20 18:15:51 +02001142 fprintf(stderr, "Unknown verbosity \"%s\"\n", verb);
Michal Vaskof3e59f12015-06-18 11:53:56 +02001143 return 1;
1144 }
1145
1146 return 0;
1147}
1148
1149int
1150cmd_quit(const char *UNUSED(arg))
1151{
1152 done = 1;
1153 return 0;
1154}
1155
1156int
1157cmd_help(const char *arg)
1158{
1159 int i;
Radek Krejciec797a32016-02-05 16:52:12 +01001160 char *args = strdup(arg);
Michal Vaskof3e59f12015-06-18 11:53:56 +02001161 char *cmd = NULL;
1162
1163 strtok(args, " ");
1164 if ((cmd = strtok(NULL, " ")) == NULL) {
1165
1166generic_help:
1167 fprintf(stdout, "Available commands:\n");
1168
1169 for (i = 0; commands[i].name; i++) {
1170 if (commands[i].helpstring != NULL) {
1171 fprintf(stdout, " %-15s %s\n", commands[i].name, commands[i].helpstring);
1172 }
1173 }
1174 } else {
1175 /* print specific help for the selected command */
1176
1177 /* get the command of the specified name */
1178 for (i = 0; commands[i].name; i++) {
1179 if (strcmp(cmd, commands[i].name) == 0) {
1180 break;
1181 }
1182 }
1183
1184 /* execute the command's help if any valid command specified */
1185 if (commands[i].name) {
1186 if (commands[i].help_func != NULL) {
1187 commands[i].help_func();
1188 } else {
1189 printf("%s\n", commands[i].helpstring);
1190 }
1191 } else {
1192 /* if unknown command specified, print the list of commands */
1193 printf("Unknown command \'%s\'\n", cmd);
1194 goto generic_help;
1195 }
1196 }
1197
Radek Krejciec797a32016-02-05 16:52:12 +01001198 free(args);
Michal Vaskof3e59f12015-06-18 11:53:56 +02001199 return 0;
1200}
1201
1202COMMAND commands[] = {
1203 {"help", cmd_help, NULL, "Display commands description"},
1204 {"add", cmd_add, cmd_add_help, "Add a new model"},
1205 {"print", cmd_print, cmd_print_help, "Print model"},
Radek Krejciba04f3e2015-11-10 19:16:01 +01001206 {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
Michal Vaskoa8c77c22016-02-04 12:08:40 +01001207 {"xpath", cmd_xpath, cmd_xpath_help, "Get data nodes satisfying an XPath expression"},
Michal Vaskof3e59f12015-06-18 11:53:56 +02001208 {"list", cmd_list, cmd_list_help, "List all the loaded models"},
Michal Vasko50cfb782015-07-07 11:37:34 +02001209 {"feature", cmd_feature, cmd_feature_help, "Print/enable/disable all/specific features of models"},
Michal Vaskof3e59f12015-06-18 11:53:56 +02001210 {"searchpath", cmd_searchpath, cmd_searchpath_help, "Set the search path for models"},
Michal Vasko6572fb42015-07-07 09:55:05 +02001211 {"clear", cmd_clear, NULL, "Clear the context - remove all the loaded models"},
Michal Vaskof3e59f12015-06-18 11:53:56 +02001212 {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
1213 {"quit", cmd_quit, NULL, "Quit the program"},
1214 /* synonyms for previous commands */
1215 {"?", cmd_help, NULL, "Display commands description"},
1216 {"exit", cmd_quit, NULL, "Quit the program"},
1217 {NULL, NULL, NULL, NULL}
1218};