blob: 67d6422d4ffacb99a606b087900f3d78c05bc718 [file] [log] [blame]
Radek Krejcied5acc52019-04-25 15:57:04 +02001/**
2 * @file commands.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libyang's yanglint tool commands
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
Radek Krejcied5acc52019-04-25 15:57:04 +020016
17#include "commands.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020018
19#include <ctype.h>
20#include <errno.h>
21#include <getopt.h>
22#include <libgen.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27
Michal Vasko5aa44c02020-06-29 11:47:02 +020028#include "compat.h"
Radek Krejcied5acc52019-04-25 15:57:04 +020029#include "libyang.h"
30
31COMMAND commands[];
32extern int done;
33extern struct ly_ctx *ctx;
34
35void
36cmd_add_help(void)
37{
38 printf("add [-i] <path-to-model> [<paths-to-other-models> ...]\n");
39 printf("\t-i - make all the imported modules implemented\n");
40}
41
42void
43cmd_load_help(void)
44{
45 printf("load [-i] <model-name> [<other-model-names> ...]\n");
46 printf("\t-i - make all the imported modules implemented\n");
47}
48
49void
50cmd_clear_help(void)
51{
52 printf("clear [<yang-library> | -e]\n");
53 printf("\t Replace the current context with an empty one, searchpaths are not kept.\n");
54 printf("\t If <yang-library> path specified, load the modules according to the yang library data.\n");
55 printf("\t Option '-e' causes ietf-yang-library will not be loaded.\n");
56}
57
58void
59cmd_print_help(void)
60{
Radek Krejcid8c0f5e2019-11-17 12:18:34 +080061 printf("print [-f (yang | yin | tree [<tree-options>] | info [-P <info-path>] [-(-s)ingle-node])] [-o <output-file>]"
Radek Krejcied5acc52019-04-25 15:57:04 +020062 " <model-name>[@<revision>]\n");
63 printf("\n");
64 printf("\ttree-options:\t--tree-print-groupings\t(print top-level groupings in a separate section)\n");
65 printf("\t \t--tree-print-uses\t(print uses nodes instead the resolved grouping nodes)\n");
66 printf("\t \t--tree-no-leafref-target\t(do not print the target nodes of leafrefs)\n");
67 printf("\t \t--tree-path <schema-path>\t(print only the specified subtree)\n");
68 printf("\t \t--tree-line-length <line-length>\t(wrap lines if longer than line-length,\n");
69 printf("\t \t\tnot a strict limit, longer lines can often appear)\n");
70 printf("\n");
Radek Krejcid8c0f5e2019-11-17 12:18:34 +080071 printf("\tinfo-path:\t<schema-path> | identity/<identity-name> | feature/<feature-name>\n");
Radek Krejcied5acc52019-04-25 15:57:04 +020072 printf("\n");
73 printf("\tschema-path:\t( /<module-name>:<node-identifier> )+\n");
74}
75
76void
77cmd_data_help(void)
78{
79 printf("data [-(-s)trict] [-t TYPE] [-d DEFAULTS] [-o <output-file>] [-f (xml | json | lyb)] [-r <running-file-name>]\n");
80 printf(" <data-file-name> [<RPC/action-data-file-name> | <yang-data name>]\n\n");
81 printf("Accepted TYPEs:\n");
82 printf("\tauto - resolve data type (one of the following) automatically (as pyang does),\n");
83 printf("\t this option is applicable only in case of XML input data.\n");
84 printf("\tdata - LYD_OPT_DATA (default value) - complete datastore including status data.\n");
85 printf("\tconfig - LYD_OPT_CONFIG - complete configuration datastore.\n");
86 printf("\tget - LYD_OPT_GET - <get> operation result.\n");
87 printf("\tgetconfig - LYD_OPT_GETCONFIG - <get-config> operation result.\n");
88 printf("\tedit - LYD_OPT_EDIT - <edit-config>'s data (content of its <config> element).\n");
89 printf("\trpc - LYD_OPT_RPC - NETCONF RPC message.\n");
90 printf("\trpcreply - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
91 printf("\tnotif - LYD_OPT_NOTIF - NETCONF Notification message.\n");
92 printf("\tyangdata - LYD_OPT_DATA_TEMPLATE - yang-data extension (last parameter mandatory in this case)\n\n");
93 printf("Accepted DEFAULTS:\n");
94 printf("\tall - add missing default nodes\n");
95 printf("\tall-tagged - add missing default nodes and mark all the default nodes with the attribute.\n");
96 printf("\ttrim - remove all nodes with a default value\n");
97 printf("\timplicit-tagged - add missing nodes and mark them with the attribute\n\n");
98 printf("Option -r:\n");
99 printf("\tOptional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the file contains running\n");
100 printf("\tconfiguration datastore data referenced from the RPC/Notification. Note that the file is\n");
101 printf("\tvalidated as 'data' TYPE. Special value '!' can be used as argument to ignore the\n");
102 printf("\texternal references.\n\n");
103 printf("\tIf an XPath expression (when/must) needs access to configuration data, you can provide\n");
104 printf("\tthem in a file, which will be parsed as 'data' TYPE.\n\n");
105}
106
107void
108cmd_xpath_help(void)
109{
110 printf("xpath [-t TYPE] [-x <additional-tree-file-name>] -e <XPath-expression>\n"
111 " <XML-data-file-name> [<JSON-rpc/action-schema-nodeid>]\n");
112 printf("Accepted TYPEs:\n");
113 printf("\tauto - resolve data type (one of the following) automatically (as pyang does),\n");
114 printf("\t this option is applicable only in case of XML input data.\n");
115 printf("\tconfig - LYD_OPT_CONFIG\n");
116 printf("\tget - LYD_OPT_GET\n");
117 printf("\tgetconfig - LYD_OPT_GETCONFIG\n");
118 printf("\tedit - LYD_OPT_EDIT\n");
119 printf("\trpc - LYD_OPT_RPC\n");
120 printf("\trpcreply - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
121 printf("\tnotif - LYD_OPT_NOTIF\n\n");
122 printf("Option -x:\n");
123 printf("\tIf RPC/action/notification/RPC reply (for TYPEs 'rpc', 'rpcreply', and 'notif') includes\n");
124 printf("\tan XPath expression (when/must) that needs access to the configuration data, you can provide\n");
125 printf("\tthem in a file, which will be parsed as 'config'.\n");
126}
127
128void
129cmd_list_help(void)
130{
131 printf("list [-f (xml | json)]\n\n");
132 printf("\tBasic list output (no -f): i - imported module, I - implemented module\n");
133}
134
135void
136cmd_feature_help(void)
137{
138 printf("feature [ -(-e)nable | -(-d)isable (* | <feature-name>[,<feature-name> ...]) ] <model-name>[@<revision>]\n");
139}
140
141void
142cmd_searchpath_help(void)
143{
144 printf("searchpath [<model-dir-path> | --clear]\n\n");
145 printf("\tThey are used to search for imports and includes of a model.\n");
146 printf("\tThe \"load\" command uses these directories to find models directly.\n");
147}
148
149void
150cmd_verb_help(void)
151{
152 printf("verb (error/0 | warning/1 | verbose/2 | debug/3)\n");
153}
154
155#ifndef NDEBUG
156
157void
158cmd_debug_help(void)
159{
160 printf("debug (dict | yang | yin | xpath | diff)+\n");
161}
162
163#endif
164
165LYS_INFORMAT
166get_schema_format(const char *path)
167{
168 char *ptr;
169
170 if ((ptr = strrchr(path, '.')) != NULL) {
171 ++ptr;
172 if (!strcmp(ptr, "yang")) {
173 return LYS_IN_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200174 } else if (!strcmp(ptr, "yin")) {
175 return LYS_IN_YIN;
Radek Krejcied5acc52019-04-25 15:57:04 +0200176 } else {
177 fprintf(stderr, "Input file in an unknown format \"%s\".\n", ptr);
178 return LYS_IN_UNKNOWN;
179 }
180 } else {
181 fprintf(stdout, "Input file \"%s\" without extension - unknown format.\n", path);
182 return LYS_IN_UNKNOWN;
183 }
184}
185
186int
187cmd_add(const char *arg)
188{
189 int path_len, ret = 1, index = 0;
190 char *path, *dir, *s, *arg_ptr;
191 const char * const *searchpaths;
192 const struct lys_module *model;
193 LYS_INFORMAT format = LYS_IN_UNKNOWN;
194
195 if (strlen(arg) < 5) {
196 cmd_add_help();
197 return 1;
198 }
199
200 arg_ptr = strdup(arg + 3 /* ignore "add" */);
201
202 for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
203 if (s[2] == '\0' || s[2] == ' ') {
Radek Krejci3fa46b62019-09-11 10:47:30 +0200204 ly_ctx_set_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200205 s[0] = s[1] = ' ';
206 }
207 }
208 s = arg_ptr;
209
210 while (arg_ptr[0] == ' ') {
211 ++arg_ptr;
212 }
213 if (strchr(arg_ptr, ' ')) {
214 path_len = strchr(arg_ptr, ' ') - arg_ptr;
215 } else {
216 path_len = strlen(arg_ptr);
217 }
218 path = strndup(arg_ptr, path_len);
219
220 searchpaths = ly_ctx_get_searchdirs(ctx);
221 if (searchpaths) {
222 for (index = 0; searchpaths[index]; index++);
223 }
224
225 while (path) {
Radek Krejci83126492020-08-15 15:40:04 +0200226 int unset_path = 1;
Radek Krejcied5acc52019-04-25 15:57:04 +0200227 format = get_schema_format(path);
228 if (format == LYS_IN_UNKNOWN) {
229 free(path);
230 goto cleanup;
231 }
232
Radek Krejci83126492020-08-15 15:40:04 +0200233 /* add temporarily also the path of the module itself */
Radek Krejcied5acc52019-04-25 15:57:04 +0200234 dir = strdup(path);
Radek Krejci83126492020-08-15 15:40:04 +0200235 if (ly_ctx_set_searchdir(ctx, dirname(dir)) == LY_EEXIST) {
236 unset_path = 0;
237 }
238 /* parse the file */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200239 lys_parse_path(ctx, path, format, &model);
Radek Krejci83126492020-08-15 15:40:04 +0200240 if (unset_path) {
241 ly_ctx_unset_searchdir(ctx, index);
242 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200243 free(path);
244 free(dir);
245
246 if (!model) {
247 /* libyang printed the error messages */
248 goto cleanup;
249 }
250
251 /* next model */
252 arg_ptr += path_len;
253 while (arg_ptr[0] == ' ') {
254 ++arg_ptr;
255 }
256 if (strchr(arg_ptr, ' ')) {
257 path_len = strchr(arg_ptr, ' ') - arg_ptr;
258 } else {
259 path_len = strlen(arg_ptr);
260 }
261
262 if (path_len) {
263 path = strndup(arg_ptr, path_len);
264 } else {
265 path = NULL;
266 }
267 }
268 if (format == LYS_IN_UNKNOWN) {
269 /* no schema on input */
270 cmd_add_help();
271 goto cleanup;
272 }
273 ret = 0;
274
275cleanup:
276 free(s);
Radek Krejci3fa46b62019-09-11 10:47:30 +0200277 ly_ctx_unset_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200278
279 return ret;
280}
281
282int
283cmd_load(const char *arg)
284{
285 int name_len, ret = 1;
286 char *name, *s, *arg_ptr;
287 const struct lys_module *model;
288
289 if (strlen(arg) < 6) {
290 cmd_load_help();
291 return 1;
292 }
293
294 arg_ptr = strdup(arg + 4 /* ignore "load" */);
295
296 for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
297 if (s[2] == '\0' || s[2] == ' ') {
Radek Krejci3fa46b62019-09-11 10:47:30 +0200298 ly_ctx_set_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200299 s[0] = s[1] = ' ';
300 }
301 }
302 s = arg_ptr;
303
304 while (arg_ptr[0] == ' ') {
305 ++arg_ptr;
306 }
307 if (strchr(arg_ptr, ' ')) {
308 name_len = strchr(arg_ptr, ' ') - arg_ptr;
309 } else {
310 name_len = strlen(arg_ptr);
311 }
312 name = strndup(arg_ptr, name_len);
313
314 while (name) {
315 model = ly_ctx_load_module(ctx, name, NULL);
316 free(name);
317 if (!model) {
318 /* libyang printed the error messages */
319 goto cleanup;
320 }
321
322 /* next model */
323 arg_ptr += name_len;
324 while (arg_ptr[0] == ' ') {
325 ++arg_ptr;
326 }
327 if (strchr(arg_ptr, ' ')) {
328 name_len = strchr(arg_ptr, ' ') - arg_ptr;
329 } else {
330 name_len = strlen(arg_ptr);
331 }
332
333 if (name_len) {
334 name = strndup(arg_ptr, name_len);
335 } else {
336 name = NULL;
337 }
338 }
339 ret = 0;
340
341cleanup:
342 free(s);
Radek Krejci3fa46b62019-09-11 10:47:30 +0200343 ly_ctx_unset_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200344
345 return ret;
346}
347
348int
349cmd_print(const char *arg)
350{
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800351 int c, argc, option_index, ret = 1, tree_ll = 0, output_opts = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200352 char **argv = NULL, *ptr, *model_name, *revision;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800353 const char *out_path = NULL, *target_path = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200354 const struct lys_module *module;
355 LYS_OUTFORMAT format = LYS_OUT_TREE;
Radek Krejci241f6b52020-05-21 18:13:49 +0200356 struct ly_out *out = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200357 static struct option long_options[] = {
358 {"help", no_argument, 0, 'h'},
359 {"format", required_argument, 0, 'f'},
360 {"output", required_argument, 0, 'o'},
361#if 0
362 {"tree-print-groupings", no_argument, 0, 'g'},
363 {"tree-print-uses", no_argument, 0, 'u'},
364 {"tree-no-leafref-target", no_argument, 0, 'n'},
365 {"tree-path", required_argument, 0, 'P'},
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800366#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200367 {"info-path", required_argument, 0, 'P'},
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800368 {"single-node", no_argument, 0, 's'},
369#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200370 {"tree-line-length", required_argument, 0, 'L'},
371#endif
372 {NULL, 0, 0, 0}
373 };
374 void *rlcd;
375
376 argc = 1;
377 argv = malloc(2*sizeof *argv);
378 *argv = strdup(arg);
379 ptr = strtok(*argv, " ");
380 while ((ptr = strtok(NULL, " "))) {
381 rlcd = realloc(argv, (argc+2)*sizeof *argv);
382 if (!rlcd) {
383 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
384 goto cleanup;
385 }
386 argv = rlcd;
387 argv[argc++] = ptr;
388 }
389 argv[argc] = NULL;
390
391 optind = 0;
392 while (1) {
393 option_index = 0;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800394 c = getopt_long(argc, argv, "chf:go:guP:sL:", long_options, &option_index);
Radek Krejcied5acc52019-04-25 15:57:04 +0200395 if (c == -1) {
396 break;
397 }
398
399 switch (c) {
400 case 'h':
401 cmd_print_help();
402 ret = 0;
403 goto cleanup;
404 case 'f':
405 if (!strcmp(optarg, "yang")) {
406 format = LYS_OUT_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200407 } else if (!strcmp(optarg, "yin")) {
408 format = LYS_OUT_YIN;
FredGand944bdc2019-11-05 21:57:07 +0800409#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200410 } else if (!strcmp(optarg, "tree")) {
411 format = LYS_OUT_TREE;
412 } else if (!strcmp(optarg, "tree-rfc")) {
413 format = LYS_OUT_TREE;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800414 output_opts |= LYS_OUTOPT_TREE_RFC;
Radek Krejcied5acc52019-04-25 15:57:04 +0200415#endif
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800416 } else if (!strcmp(optarg, "info")) {
417 format = LYS_OUT_YANG_COMPILED;
Radek Krejcied5acc52019-04-25 15:57:04 +0200418 } else {
419 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
420 goto cleanup;
421 }
422 break;
423 case 'o':
424 if (out_path) {
425 fprintf(stderr, "Output specified twice.\n");
426 goto cleanup;
427 }
428 out_path = optarg;
429 break;
430#if 0
431 case 'g':
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800432 output_opts |= LYS_OUTOPT_TREE_GROUPING;
Radek Krejcied5acc52019-04-25 15:57:04 +0200433 break;
434 case 'u':
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800435 output_opts |= LYS_OUTOPT_TREE_USES;
Radek Krejcied5acc52019-04-25 15:57:04 +0200436 break;
437 case 'n':
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800438 output_opts |= LYS_OUTOPT_TREE_NO_LEAFREF;
Radek Krejcied5acc52019-04-25 15:57:04 +0200439 break;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800440#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200441 case 'P':
442 target_path = optarg;
443 break;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800444 case 's':
Radek Krejci4fa6ebf2019-11-21 13:34:35 +0800445 output_opts |= LYS_OUTPUT_NO_SUBSTMT;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800446 break;
447#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200448 case 'L':
449 tree_ll = atoi(optarg);
450 break;
451#endif
452 case '?':
453 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
454 goto cleanup;
455 }
456 }
457
458 /* file name */
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800459 if (optind == argc && !target_path) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200460 fprintf(stderr, "Missing the module name.\n");
461 goto cleanup;
462 }
463
Radek Krejci77954e82019-04-30 13:51:29 +0200464#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200465 /* tree fromat with or without gropings */
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800466 if ((output_opts || tree_ll) && format != LYS_OUT_TREE) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200467 fprintf(stderr, "--tree options take effect only in case of the tree output format.\n");
468 }
Radek Krejci77954e82019-04-30 13:51:29 +0200469#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200470
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800471 if (!target_path) {
472 /* module, revision */
473 model_name = argv[optind];
474 revision = NULL;
475 if (strchr(model_name, '@')) {
476 revision = strchr(model_name, '@');
477 revision[0] = '\0';
478 ++revision;
479 }
480
481 if (revision) {
482 module = ly_ctx_get_module(ctx, model_name, revision);
483 } else {
484 module = ly_ctx_get_module_latest(ctx, model_name);
485 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200486#if 0
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800487 if (!module) {
488 /* not a module, try to find it as a submodule */
489 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
490 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200491#endif
492
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800493 if (!module) {
494 if (revision) {
495 fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
496 } else {
497 fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
498 }
499 goto cleanup;
Radek Krejcied5acc52019-04-25 15:57:04 +0200500 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200501 }
502
503 if (out_path) {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200504 ret = ly_out_new_filepath(out_path, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100505 } else {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200506 ret = ly_out_new_file(stdout, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100507 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200508 if (ret) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100509 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
510 goto cleanup;
Radek Krejcied5acc52019-04-25 15:57:04 +0200511 }
512
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800513 if (target_path) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100514 const struct lysc_node *node = lys_find_node(ctx, NULL, target_path);
515 if (node) {
516 ret = lys_print_node(out, node, format, tree_ll, output_opts);
517 } else {
518 fprintf(stderr, "The requested schema node \"%s\" does not exists.\n", target_path);
519 }
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800520 } else {
Michal Vasko7c8439f2020-08-05 13:25:19 +0200521 ret = lys_print_module(out, module, format, tree_ll, output_opts);
Radek Krejcied5acc52019-04-25 15:57:04 +0200522 }
523
524cleanup:
525 free(*argv);
526 free(argv);
527
Radek Krejci241f6b52020-05-21 18:13:49 +0200528 ly_out_free(out, NULL, out_path ? 1 : 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200529
530 return ret;
531}
Radek Krejcie7b95092019-05-15 11:03:07 +0200532
Radek Krejcied5acc52019-04-25 15:57:04 +0200533static int
Michal Vaskof03ed032020-03-04 13:31:44 +0100534parse_data(char *filepath, int *options, const struct lyd_node *tree, const char *rpc_act_file,
Radek Krejcied5acc52019-04-25 15:57:04 +0200535 struct lyd_node **result)
536{
Radek Krejcied5acc52019-04-25 15:57:04 +0200537 struct lyd_node *data = NULL, *rpc_act = NULL;
538 int opts = *options;
Radek Krejci7931b192020-06-25 17:05:03 +0200539 struct ly_in *in;
Radek Krejcied5acc52019-04-25 15:57:04 +0200540
Radek Krejci7931b192020-06-25 17:05:03 +0200541 if (ly_in_new_filepath(filepath, 0, &in)) {
542 fprintf(stderr, "Unable to open input YANG data file \"%s\".", filepath);
Radek Krejcied5acc52019-04-25 15:57:04 +0200543 return EXIT_FAILURE;
544 }
545
Radek Krejcie7b95092019-05-15 11:03:07 +0200546#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200547 if ((opts & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
548 /* automatically detect data type from the data top level */
549 if (informat != LYD_XML) {
550 fprintf(stderr, "Only XML data can be automatically explored.\n");
551 return EXIT_FAILURE;
552 }
553
554 xml = lyxml_parse_path(ctx, filepath, 0);
555 if (!xml) {
556 fprintf(stderr, "Failed to parse XML data for automatic type detection.\n");
557 return EXIT_FAILURE;
558 }
559
560 /* NOTE: namespace is ignored to simplify usage of this feature */
561
562 if (!strcmp(xml->name, "data")) {
563 fprintf(stdout, "Parsing %s as complete datastore.\n", filepath);
564 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_ADD_YANGLIB;
565 } else if (!strcmp(xml->name, "config")) {
566 fprintf(stdout, "Parsing %s as config data.\n", filepath);
567 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
568 } else if (!strcmp(xml->name, "get-reply")) {
569 fprintf(stdout, "Parsing %s as <get> reply data.\n", filepath);
570 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
571 } else if (!strcmp(xml->name, "get-config-reply")) {
572 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", filepath);
573 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
574 } else if (!strcmp(xml->name, "edit-config")) {
575 fprintf(stdout, "Parsing %s as <edit-config> data.\n", filepath);
576 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
577 } else if (!strcmp(xml->name, "rpc")) {
578 fprintf(stdout, "Parsing %s as <rpc> data.\n", filepath);
579 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
580 } else if (!strcmp(xml->name, "rpc-reply")) {
581 if (!rpc_act_file) {
582 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
583 lyxml_free(ctx, xml);
584 return EXIT_FAILURE;
585 }
586 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", filepath);
587 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
588 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, val_tree);
589 if (!rpc_act) {
590 fprintf(stderr, "Failed to parse RPC/action.\n");
591 lyxml_free(ctx, xml);
592 return EXIT_FAILURE;
593 }
594 } else if (!strcmp(xml->name, "notification")) {
595 fprintf(stdout, "Parsing %s as <notification> data.\n", filepath);
596 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
597 } else if (!strcmp(xml->name, "yang-data")) {
598 fprintf(stdout, "Parsing %s as <yang-data> data.\n", filepath);
599 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
600 if (!rpc_act_file) {
601 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
602 lyxml_free(ctx, xml);
603 return EXIT_FAILURE;
604 }
605 } else {
606 fprintf(stderr, "Invalid top-level element for automatic data type recognition.\n");
607 lyxml_free(ctx, xml);
608 return EXIT_FAILURE;
609 }
610
611 if (opts & LYD_OPT_RPCREPLY) {
612 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act, val_tree);
613 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
614 data = lyd_parse_xml(ctx, &xml->child, opts, val_tree);
615 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
616 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act_file);
617 } else {
618 data = lyd_parse_xml(ctx, &xml->child, opts);
619 }
620 lyxml_free(ctx, xml);
621 } else {
622 if (opts & LYD_OPT_RPCREPLY) {
623 if (!rpc_act_file) {
624 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
625 return EXIT_FAILURE;
626 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200627 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200628 if (!rpc_act) {
629 fprintf(stderr, "Failed to parse RPC/action.\n");
630 return EXIT_FAILURE;
631 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200632 if (trees) {
633 const struct lyd_node **trees_new;
634 unsigned int u;
635 trees_new = lyd_trees_new(1, rpc_act);
636
637 LY_ARRAY_FOR(trees, u) {
638 trees_new = lyd_trees_add(trees_new, trees[u]);
639 }
640 lyd_trees_free(trees, 0);
641 trees = trees_new;
642 } else {
643 trees = lyd_trees_new(1, rpc_act);
644 }
645 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200646 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200647 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200648 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
649 if (!rpc_act_file) {
650 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
651 return EXIT_FAILURE;
652 }
653 data = lyd_parse_path(ctx, filepath, informat, opts, rpc_act_file);
654 } else {
Michal Vaskoa3881362020-01-21 15:57:35 +0100655#endif
Radek Krejci7931b192020-06-25 17:05:03 +0200656
Radek Krejci5536d282020-08-04 23:27:44 +0200657 lyd_parse_data(ctx, in, 0, opts, LYD_VALIDATE_PRESENT, &data);
Radek Krejcie7b95092019-05-15 11:03:07 +0200658#if 0
Michal Vaskoa3881362020-01-21 15:57:35 +0100659 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200660 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200661#endif
Radek Krejci7931b192020-06-25 17:05:03 +0200662 ly_in_free(in, 0);
663
Radek Krejcie7b95092019-05-15 11:03:07 +0200664 lyd_free_all(rpc_act);
Radek Krejcied5acc52019-04-25 15:57:04 +0200665
Radek Krejcie7b95092019-05-15 11:03:07 +0200666 if (ly_err_first(ctx)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200667 fprintf(stderr, "Failed to parse data.\n");
Radek Krejcie7b95092019-05-15 11:03:07 +0200668 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200669 return EXIT_FAILURE;
670 }
671
672 *result = data;
673 *options = opts;
674 return EXIT_SUCCESS;
675}
676
677int
678cmd_data(const char *arg)
679{
680 int c, argc, option_index, ret = 1;
681 int options = 0, printopt = 0;
682 char **argv = NULL, *ptr;
683 const char *out_path = NULL;
Michal Vaskof03ed032020-03-04 13:31:44 +0100684 struct lyd_node *data = NULL;
685 struct lyd_node *tree = NULL;
Michal Vasko52927e22020-03-16 17:26:14 +0100686 LYD_FORMAT outformat = 0;
Radek Krejci241f6b52020-05-21 18:13:49 +0200687 struct ly_out *out = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200688 static struct option long_options[] = {
689 {"defaults", required_argument, 0, 'd'},
690 {"help", no_argument, 0, 'h'},
691 {"format", required_argument, 0, 'f'},
692 {"option", required_argument, 0, 't'},
693 {"output", required_argument, 0, 'o'},
694 {"running", required_argument, 0, 'r'},
695 {"strict", no_argument, 0, 's'},
696 {NULL, 0, 0, 0}
697 };
698 void *rlcd;
699
700 argc = 1;
701 argv = malloc(2*sizeof *argv);
702 *argv = strdup(arg);
703 ptr = strtok(*argv, " ");
704 while ((ptr = strtok(NULL, " "))) {
705 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
706 if (!rlcd) {
707 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
708 goto cleanup;
709 }
710 argv = rlcd;
711 argv[argc++] = ptr;
712 }
713 argv[argc] = NULL;
714
715 optind = 0;
716 while (1) {
717 option_index = 0;
718 c = getopt_long(argc, argv, "d:hf:o:st:r:", long_options, &option_index);
719 if (c == -1) {
720 break;
721 }
722
723 switch (c) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200724#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200725 case 'd':
726 if (!strcmp(optarg, "all")) {
727 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL;
728 } else if (!strcmp(optarg, "all-tagged")) {
729 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
730 } else if (!strcmp(optarg, "trim")) {
731 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_TRIM;
732 } else if (!strcmp(optarg, "implicit-tagged")) {
733 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
734 }
735 break;
Radek Krejcie7b95092019-05-15 11:03:07 +0200736#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200737 case 'h':
738 cmd_data_help();
739 ret = 0;
740 goto cleanup;
741 case 'f':
742 if (!strcmp(optarg, "xml")) {
743 outformat = LYD_XML;
744 } else if (!strcmp(optarg, "json")) {
745 outformat = LYD_JSON;
746 } else if (!strcmp(optarg, "lyb")) {
747 outformat = LYD_LYB;
748 } else {
749 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
750 goto cleanup;
751 }
752 break;
753 case 'o':
754 if (out_path) {
755 fprintf(stderr, "Output specified twice.\n");
756 goto cleanup;
757 }
758 out_path = optarg;
759 break;
Michal Vaskoa3881362020-01-21 15:57:35 +0100760#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200761 case 'r':
Radek Krejcied5acc52019-04-25 15:57:04 +0200762 if (optarg[0] == '!') {
763 /* ignore extenral dependencies to the running datastore */
764 options |= LYD_OPT_NOEXTDEPS;
765 } else {
766 /* external file with the running datastore */
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200767 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_DATA_NO_YANGLIB, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200768 if (!val_tree) {
769 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
770 goto cleanup;
771 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200772 if (!trees) {
773 trees = lyd_trees_new(1, val_tree);
774 } else {
775 trees = lyd_trees_add(trees, val_tree);
776 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200777 }
778 break;
779 case 's':
780 options |= LYD_OPT_STRICT;
781 options |= LYD_OPT_OBSOLETE;
782 break;
Michal Vaskoa3881362020-01-21 15:57:35 +0100783#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200784 case 't':
785 if (!strcmp(optarg, "auto")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100786 /* no flags */
Radek Krejcied5acc52019-04-25 15:57:04 +0200787 } else if (!strcmp(optarg, "data")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100788 /* no flags */
Michal Vaskob36053d2020-03-26 15:49:30 +0100789 /*} else if (!strcmp(optarg, "config")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100790 options |= LYD_OPT_CONFIG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200791 } else if (!strcmp(optarg, "get")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100792 options |= LYD_OPT_GET;
Radek Krejcied5acc52019-04-25 15:57:04 +0200793 } else if (!strcmp(optarg, "getconfig")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100794 options |= LYD_OPT_GETCONFIG;
Michal Vaskob36053d2020-03-26 15:49:30 +0100795 } else if (!strcmp(optarg, "edit")) {
Michal Vasko9f96a052020-03-10 09:41:45 +0100796 options |= LYD_OPT_EDIT;*/
Radek Krejcied5acc52019-04-25 15:57:04 +0200797 } else {
798 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
799 cmd_data_help();
800 goto cleanup;
801 }
802 break;
803 case '?':
804 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
805 goto cleanup;
806 }
807 }
808
809 /* file name */
810 if (optind == argc) {
811 fprintf(stderr, "Missing the data file name.\n");
812 goto cleanup;
813 }
814
Michal Vaskof03ed032020-03-04 13:31:44 +0100815 if (parse_data(argv[optind], &options, tree, argv[optind + 1], &data)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200816 goto cleanup;
817 }
818
819 if (out_path) {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200820 ret = ly_out_new_filepath(out_path, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100821 } else {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200822 ret = ly_out_new_file(stdout, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100823 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200824 if (ret) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100825 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
826 goto cleanup;
Radek Krejcied5acc52019-04-25 15:57:04 +0200827 }
828
Michal Vasko52927e22020-03-16 17:26:14 +0100829 if (outformat) {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200830 ret = lyd_print_all(out, data, outformat, LYD_PRINT_FORMAT | printopt);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200831 ret = ret < 0 ? ret * (-1) : 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200832 }
833
Radek Krejcied5acc52019-04-25 15:57:04 +0200834cleanup:
835 free(*argv);
836 free(argv);
837
Radek Krejci241f6b52020-05-21 18:13:49 +0200838 ly_out_free(out, NULL, out_path ? 1 : 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200839
Radek Krejcie7b95092019-05-15 11:03:07 +0200840 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200841
842 return ret;
843}
Radek Krejcie7b95092019-05-15 11:03:07 +0200844#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200845int
846cmd_xpath(const char *arg)
847{
848 int c, argc, option_index, ret = 1, long_str;
849 char **argv = NULL, *ptr, *expr = NULL;
850 unsigned int i, j;
851 int options = 0;
852 struct lyd_node *data = NULL, *node, *val_tree = NULL;
853 struct lyd_node_leaf_list *key;
854 struct ly_set *set;
855 static struct option long_options[] = {
856 {"help", no_argument, 0, 'h'},
857 {"expr", required_argument, 0, 'e'},
858 {NULL, 0, 0, 0}
859 };
860 void *rlcd;
861
862 long_str = 0;
863 argc = 1;
864 argv = malloc(2 * sizeof *argv);
865 *argv = strdup(arg);
866 ptr = strtok(*argv, " ");
867 while ((ptr = strtok(NULL, " "))) {
868 if (long_str) {
869 ptr[-1] = ' ';
870 if (ptr[strlen(ptr) - 1] == long_str) {
871 long_str = 0;
872 ptr[strlen(ptr) - 1] = '\0';
873 }
874 } else {
875 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
876 if (!rlcd) {
877 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
878 goto cleanup;
879 }
880 argv = rlcd;
881 argv[argc] = ptr;
882 if (ptr[0] == '"') {
883 long_str = '"';
884 ++argv[argc];
885 }
886 if (ptr[0] == '\'') {
887 long_str = '\'';
888 ++argv[argc];
889 }
890 if (ptr[strlen(ptr) - 1] == long_str) {
891 long_str = 0;
892 ptr[strlen(ptr) - 1] = '\0';
893 }
894 ++argc;
895 }
896 }
897 argv[argc] = NULL;
898
899 optind = 0;
900 while (1) {
901 option_index = 0;
902 c = getopt_long(argc, argv, "he:t:x:", long_options, &option_index);
903 if (c == -1) {
904 break;
905 }
906
907 switch (c) {
908 case 'h':
909 cmd_xpath_help();
910 ret = 0;
911 goto cleanup;
912 case 'e':
913 expr = optarg;
914 break;
915 case 't':
916 if (!strcmp(optarg, "auto")) {
917 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
918 } else if (!strcmp(optarg, "config")) {
919 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
920 } else if (!strcmp(optarg, "get")) {
921 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
922 } else if (!strcmp(optarg, "getconfig")) {
923 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
924 } else if (!strcmp(optarg, "edit")) {
925 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
926 } else if (!strcmp(optarg, "rpc")) {
927 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
928 } else if (!strcmp(optarg, "rpcreply")) {
929 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
930 } else if (!strcmp(optarg, "notif")) {
931 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
932 } else if (!strcmp(optarg, "yangdata")) {
933 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
934 } else {
935 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
936 cmd_data_help();
937 goto cleanup;
938 }
939 break;
940 case 'x':
941 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_CONFIG);
942 if (!val_tree) {
943 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
944 goto cleanup;
945 }
946 break;
947 case '?':
948 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
949 goto cleanup;
950 }
951 }
952
953 if (optind == argc) {
954 fprintf(stderr, "Missing the file with data.\n");
955 goto cleanup;
956 }
957
958 if (!expr) {
959 fprintf(stderr, "Missing the XPath expression.\n");
960 goto cleanup;
961 }
962
963 if (parse_data(argv[optind], &options, val_tree, argv[optind + 1], &data)) {
964 goto cleanup;
965 }
966
967 if (!(set = lyd_find_path(data, expr))) {
968 goto cleanup;
969 }
970
971 /* print result */
972 printf("Result:\n");
973 if (!set->number) {
974 printf("\tEmpty\n");
975 } else {
976 for (i = 0; i < set->number; ++i) {
977 node = set->set.d[i];
978 switch (node->schema->nodetype) {
979 case LYS_CONTAINER:
980 printf("\tContainer ");
981 break;
982 case LYS_LEAF:
983 printf("\tLeaf ");
984 break;
985 case LYS_LEAFLIST:
986 printf("\tLeaflist ");
987 break;
988 case LYS_LIST:
989 printf("\tList ");
990 break;
991 case LYS_ANYXML:
992 printf("\tAnyxml ");
993 break;
994 case LYS_ANYDATA:
995 printf("\tAnydata ");
996 break;
997 default:
998 printf("\tUnknown ");
999 break;
1000 }
1001 printf("\"%s\"", node->schema->name);
1002 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
1003 printf(" (val: %s)", ((struct lyd_node_leaf_list *)node)->value_str);
1004 } else if (node->schema->nodetype == LYS_LIST) {
1005 key = (struct lyd_node_leaf_list *)node->child;
1006 printf(" (");
1007 for (j = 0; j < ((struct lys_node_list *)node->schema)->keys_size; ++j) {
1008 if (j) {
1009 printf(" ");
1010 }
1011 printf("\"%s\": %s", key->schema->name, key->value_str);
1012 key = (struct lyd_node_leaf_list *)key->next;
1013 }
1014 printf(")");
1015 }
1016 printf("\n");
1017 }
1018 }
1019 printf("\n");
1020
1021 ly_set_free(set);
1022 ret = 0;
1023
1024cleanup:
1025 free(*argv);
1026 free(argv);
1027
1028 lyd_free_withsiblings(data);
1029
1030 return ret;
1031}
1032
1033int
1034print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
1035{
1036 struct lyd_node *ylib;
1037 uint32_t idx = 0, has_modules = 0;
1038 uint8_t u;
1039 const struct lys_module *mod;
1040
1041 if (outformat != LYD_UNKNOWN) {
1042 ylib = ly_ctx_info(ctx);
1043 if (!ylib) {
1044 fprintf(stderr, "Getting context info (ietf-yang-library data) failed.\n");
1045 return 1;
1046 }
1047
1048 lyd_print_file(out, ylib, outformat, LYP_WITHSIBLINGS | LYP_FORMAT);
1049 lyd_free_withsiblings(ylib);
1050 return 0;
1051 }
1052
1053 /* iterate schemas in context and provide just the basic info */
1054 fprintf(out, "List of the loaded models:\n");
1055 while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
1056 has_modules++;
1057
1058 /* conformance print */
1059 if (mod->implemented) {
1060 fprintf(out, "\tI");
1061 } else {
1062 fprintf(out, "\ti");
1063 }
1064
1065 /* module print */
1066 fprintf(out, " %s", mod->name);
1067 if (mod->rev_size) {
1068 fprintf(out, "@%s", mod->rev[0].date);
1069 }
1070
1071 /* submodules print */
1072 if (mod->inc_size) {
1073 fprintf(out, " (");
1074 for (u = 0; u < mod->inc_size; u++) {
1075 fprintf(out, "%s%s", !u ? "" : ",", mod->inc[u].submodule->name);
1076 if (mod->inc[u].submodule->rev_size) {
1077 fprintf(out, "@%s", mod->inc[u].submodule->rev[0].date);
1078 }
1079 }
1080 fprintf(out, ")");
1081 }
1082
1083 /* finish the line */
1084 fprintf(out, "\n");
1085 }
1086
1087 if (!has_modules) {
1088 fprintf(out, "\t(none)\n");
1089 }
1090
1091 return 0;
1092}
1093
1094int
1095cmd_list(const char *arg)
1096{
1097 char **argv = NULL, *ptr;
1098 int c, argc, option_index;
1099 LYD_FORMAT outformat = LYD_UNKNOWN;
1100 static struct option long_options[] = {
1101 {"help", no_argument, 0, 'h'},
1102 {"format", required_argument, 0, 'f'},
1103 {NULL, 0, 0, 0}
1104 };
1105 void *rlcd;
1106
1107 argc = 1;
1108 argv = malloc(2*sizeof *argv);
1109 *argv = strdup(arg);
1110 ptr = strtok(*argv, " ");
1111 while ((ptr = strtok(NULL, " "))) {
1112 rlcd = realloc(argv, (argc+2)*sizeof *argv);
1113 if (!rlcd) {
1114 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1115 goto error;
1116 }
1117 argv = rlcd;
1118 argv[argc++] = ptr;
1119 }
1120 argv[argc] = NULL;
1121
1122 optind = 0;
1123 while (1) {
1124 option_index = 0;
1125 c = getopt_long(argc, argv, "hf:", long_options, &option_index);
1126 if (c == -1) {
1127 break;
1128 }
1129
1130 switch (c) {
1131 case 'h':
1132 cmd_data_help();
1133 free(*argv);
1134 free(argv);
1135 return 0;
1136 case 'f':
1137 if (!strcmp(optarg, "xml")) {
1138 outformat = LYD_XML;
1139 } else if (!strcmp(optarg, "json")) {
1140 outformat = LYD_JSON;
1141 } else {
1142 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
1143 goto error;
1144 }
1145 break;
1146 case '?':
1147 /* getopt_long() prints message */
1148 goto error;
1149 }
1150 }
1151 if (optind != argc) {
1152 fprintf(stderr, "Unknown parameter \"%s\"\n", argv[optind]);
1153error:
1154 free(*argv);
1155 free(argv);
1156 return 1;
1157 }
1158 free(*argv);
1159 free(argv);
1160
1161 return print_list(stdout, ctx, outformat);
1162}
1163#endif
1164int
1165cmd_feature(const char *arg)
1166{
1167 int c, argc, option_index, ret = 1, task = 0;
1168 char **argv = NULL, *ptr, *model_name, *revision, *feat_names = NULL;
1169 const struct lys_module *module;
1170 static struct option long_options[] = {
1171 {"help", no_argument, 0, 'h'},
1172 {"enable", required_argument, 0, 'e'},
1173 {"disable", required_argument, 0, 'd'},
1174 {NULL, 0, 0, 0}
1175 };
1176 void *rlcd;
1177
1178 argc = 1;
1179 argv = malloc(2*sizeof *argv);
1180 *argv = strdup(arg);
1181 ptr = strtok(*argv, " ");
1182 while ((ptr = strtok(NULL, " "))) {
1183 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
1184 if (!rlcd) {
1185 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1186 goto cleanup;
1187 }
1188 argv = rlcd;
1189 argv[argc++] = ptr;
1190 }
1191 argv[argc] = NULL;
1192
1193 optind = 0;
1194 while (1) {
1195 option_index = 0;
1196 c = getopt_long(argc, argv, "he:d:", long_options, &option_index);
1197 if (c == -1) {
1198 break;
1199 }
1200
1201 switch (c) {
1202 case 'h':
1203 cmd_feature_help();
1204 ret = 0;
1205 goto cleanup;
1206 case 'e':
1207 if (task) {
1208 fprintf(stderr, "Only one of enable or disable can be specified.\n");
1209 goto cleanup;
1210 }
1211 task = 1;
1212 feat_names = optarg;
1213 break;
1214 case 'd':
1215 if (task) {
1216 fprintf(stderr, "Only one of enable, or disable can be specified.\n");
1217 goto cleanup;
1218 }
1219 task = 2;
1220 feat_names = optarg;
1221 break;
1222 case '?':
1223 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
1224 goto cleanup;
1225 }
1226 }
1227
1228 /* module name */
1229 if (optind == argc) {
1230 fprintf(stderr, "Missing the module name.\n");
1231 goto cleanup;
1232 }
1233
1234 revision = NULL;
1235 model_name = argv[optind];
1236 if (strchr(model_name, '@')) {
1237 revision = strchr(model_name, '@');
1238 revision[0] = '\0';
1239 ++revision;
1240 }
1241
Radek Krejci3d46f612020-08-13 12:01:53 +02001242 if (!revision) {
1243 module = ly_ctx_get_module_implemented(ctx, model_name);
1244 } else {
1245 module = ly_ctx_get_module(ctx, model_name, revision);
1246 if (module && !module->implemented) {
1247 module = NULL;
1248 }
1249 }
Radek Krejcied5acc52019-04-25 15:57:04 +02001250#if 0
1251 if (!module) {
1252 /* not a module, try to find it as a submodule */
1253 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
1254 }
1255#endif
1256
1257 if (module == NULL) {
1258 if (revision) {
Radek Krejci3d46f612020-08-13 12:01:53 +02001259 fprintf(stderr, "No implemented (sub)module \"%s\" in revision %s found.\n", model_name, revision);
Radek Krejcied5acc52019-04-25 15:57:04 +02001260 } else {
Radek Krejci3d46f612020-08-13 12:01:53 +02001261 fprintf(stderr, "No implemented (sub)module \"%s\" found.\n", model_name);
Radek Krejcied5acc52019-04-25 15:57:04 +02001262 }
1263 goto cleanup;
1264 }
1265
1266 if (!task) {
Radek Krejci7eb54ba2020-05-18 16:30:04 +02001267 size_t len, max_len = 0;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02001268 LY_ARRAY_COUNT_TYPE u;
Radek Krejcied5acc52019-04-25 15:57:04 +02001269 struct lysc_feature *features;
1270
1271 printf("%s features:\n", module->name);
1272
1273 if (module->compiled) {
1274 features = module->compiled->features;
1275 } else {
Michal Vasko33ff9422020-07-03 09:50:39 +02001276 features = module->dis_features;
Radek Krejcied5acc52019-04-25 15:57:04 +02001277 }
1278
1279 /* get the max len */
1280 LY_ARRAY_FOR(features, u) {
1281 len = strlen(features[u].name);
1282 if (len > max_len) {
1283 max_len = len;
1284 }
1285 }
1286
1287 LY_ARRAY_FOR(features, u) {
Radek Krejci7eb54ba2020-05-18 16:30:04 +02001288 printf("\t%-*s (%s)\n", (int)max_len, features[u].name, (features[u].flags & LYS_FENABLED) ? "on" : "off");
Radek Krejcied5acc52019-04-25 15:57:04 +02001289 }
1290 if (!u) {
1291 printf("\t(none)\n");
1292 }
1293 } else {
1294 feat_names = strtok(feat_names, ",");
1295 while (feat_names) {
1296 if (((task == 1) && lys_feature_enable(module, feat_names))
1297 || ((task == 2) && lys_feature_disable(module, feat_names))) {
1298 fprintf(stderr, "Feature \"%s\" not found.\n", feat_names);
1299 ret = 1;
1300 }
1301 feat_names = strtok(NULL, ",");
1302 }
1303 }
1304
1305cleanup:
1306 free(*argv);
1307 free(argv);
1308
1309 return ret;
1310}
1311
1312int
1313cmd_searchpath(const char *arg)
1314{
1315 const char *path;
1316 const char * const *searchpaths;
1317 int index;
1318 struct stat st;
1319
1320 for (path = strchr(arg, ' '); path && (path[0] == ' '); ++path);
1321 if (!path || (path[0] == '\0')) {
1322 searchpaths = ly_ctx_get_searchdirs(ctx);
1323 if (searchpaths) {
1324 for (index = 0; searchpaths[index]; index++) {
1325 fprintf(stdout, "%s\n", searchpaths[index]);
1326 }
1327 }
1328 return 0;
1329 }
1330
1331 if ((!strncmp(path, "-h", 2) && (path[2] == '\0' || path[2] == ' ')) ||
1332 (!strncmp(path, "--help", 6) && (path[6] == '\0' || path[6] == ' '))) {
1333 cmd_searchpath_help();
1334 return 0;
1335 } else if (!strncmp(path, "--clear", 7) && (path[7] == '\0' || path[7] == ' ')) {
1336 ly_ctx_unset_searchdirs(ctx, NULL);
1337 return 0;
1338 }
1339
1340 if (stat(path, &st) == -1) {
1341 fprintf(stderr, "Failed to stat the search path (%s).\n", strerror(errno));
1342 return 1;
1343 }
1344 if (!S_ISDIR(st.st_mode)) {
1345 fprintf(stderr, "\"%s\" is not a directory.\n", path);
1346 return 1;
1347 }
1348
1349 ly_ctx_set_searchdir(ctx, path);
1350
1351 return 0;
1352}
1353
1354int
1355cmd_clear(const char *arg)
1356{
1357 struct ly_ctx *ctx_new;
1358 int options = 0;
1359#if 0
1360 int i;
1361 char *ylpath;
1362 const char * const *searchpaths;
1363 LYD_FORMAT format;
1364
1365 /* get optional yang library file name */
1366 for (i = 5; arg[i] && isspace(arg[i]); i++);
1367 if (arg[i]) {
1368 if (arg[i] == '-' && arg[i + 1] == 'e') {
1369 options = LY_CTX_NOYANGLIBRARY;
1370 goto create_empty;
1371 } else {
1372 ylpath = strdup(&arg[i]);
1373 format = detect_data_format(ylpath);
1374 if (format == LYD_UNKNOWN) {
1375 free(ylpath);
1376 fprintf(stderr, "Unable to resolve format of the yang library file, please add \".xml\" or \".json\" suffix.\n");
1377 goto create_empty;
1378 }
1379 searchpaths = ly_ctx_get_searchdirs(ctx);
1380 ctx_new = ly_ctx_new_ylpath(searchpaths ? searchpaths[0] : NULL, ylpath, format, 0);
1381 free(ylpath);
1382 }
1383 } else {
1384create_empty:
1385#else
Radek Krejci92f5ce92019-09-06 16:25:43 +02001386 (void) arg; /* TODO yang-library support */
Radek Krejcied5acc52019-04-25 15:57:04 +02001387 {
1388#endif
1389 ly_ctx_new(NULL, options, &ctx_new);
1390 }
1391
1392 if (!ctx_new) {
1393 fprintf(stderr, "Failed to create context.\n");
1394 return 1;
1395 }
1396
1397 /* final switch */
1398 ly_ctx_destroy(ctx, NULL);
1399 ctx = ctx_new;
1400
1401 return 0;
1402}
1403
1404int
1405cmd_verb(const char *arg)
1406{
1407 const char *verb;
1408 if (strlen(arg) < 5) {
1409 cmd_verb_help();
1410 return 1;
1411 }
1412
1413 verb = arg + 5;
1414 if (!strcmp(verb, "error") || !strcmp(verb, "0")) {
1415 ly_verb(LY_LLERR);
1416#ifndef NDEBUG
1417 ly_verb_dbg(0);
1418#endif
1419 } else if (!strcmp(verb, "warning") || !strcmp(verb, "1")) {
1420 ly_verb(LY_LLWRN);
1421#ifndef NDEBUG
1422 ly_verb_dbg(0);
1423#endif
1424 } else if (!strcmp(verb, "verbose") || !strcmp(verb, "2")) {
1425 ly_verb(LY_LLVRB);
1426#ifndef NDEBUG
1427 ly_verb_dbg(0);
1428#endif
1429 } else if (!strcmp(verb, "debug") || !strcmp(verb, "3")) {
1430 ly_verb(LY_LLDBG);
1431#ifndef NDEBUG
1432 ly_verb_dbg(LY_LDGDICT | LY_LDGYANG | LY_LDGYIN | LY_LDGXPATH | LY_LDGDIFF);
1433#endif
1434 } else {
1435 fprintf(stderr, "Unknown verbosity \"%s\"\n", verb);
1436 return 1;
1437 }
1438
1439 return 0;
1440}
1441
1442#ifndef NDEBUG
1443
1444int
1445cmd_debug(const char *arg)
1446{
1447 const char *beg, *end;
1448 int grps = 0;
1449 if (strlen(arg) < 6) {
1450 cmd_debug_help();
1451 return 1;
1452 }
1453
1454 end = arg + 6;
1455 while (end[0]) {
1456 for (beg = end; isspace(beg[0]); ++beg);
1457 if (!beg[0]) {
1458 break;
1459 }
1460
1461 for (end = beg; (end[0] && !isspace(end[0])); ++end);
1462
1463 if (!strncmp(beg, "dict", end - beg)) {
1464 grps |= LY_LDGDICT;
1465 } else if (!strncmp(beg, "yang", end - beg)) {
1466 grps |= LY_LDGYANG;
1467 } else if (!strncmp(beg, "yin", end - beg)) {
1468 grps |= LY_LDGYIN;
1469 } else if (!strncmp(beg, "xpath", end - beg)) {
1470 grps |= LY_LDGXPATH;
1471 } else if (!strncmp(beg, "diff", end - beg)) {
1472 grps |= LY_LDGDIFF;
1473 } else {
1474 fprintf(stderr, "Unknown debug group \"%.*s\"\n", (int)(end - beg), beg);
1475 return 1;
1476 }
1477 }
1478 ly_verb_dbg(grps);
1479
1480 return 0;
1481}
1482
1483#endif
1484
1485int
1486cmd_quit(const char *UNUSED(arg))
1487{
1488 done = 1;
1489 return 0;
1490}
1491
1492int
1493cmd_help(const char *arg)
1494{
1495 int i;
1496 char *args = strdup(arg);
1497 char *cmd = NULL;
1498
1499 strtok(args, " ");
1500 if ((cmd = strtok(NULL, " ")) == NULL) {
1501
1502generic_help:
1503 fprintf(stdout, "Available commands:\n");
1504
1505 for (i = 0; commands[i].name; i++) {
1506 if (commands[i].helpstring != NULL) {
1507 fprintf(stdout, " %-15s %s\n", commands[i].name, commands[i].helpstring);
1508 }
1509 }
1510 } else {
1511 /* print specific help for the selected command */
1512
1513 /* get the command of the specified name */
1514 for (i = 0; commands[i].name; i++) {
1515 if (strcmp(cmd, commands[i].name) == 0) {
1516 break;
1517 }
1518 }
1519
1520 /* execute the command's help if any valid command specified */
1521 if (commands[i].name) {
1522 if (commands[i].help_func != NULL) {
1523 commands[i].help_func();
1524 } else {
1525 printf("%s\n", commands[i].helpstring);
1526 }
1527 } else {
1528 /* if unknown command specified, print the list of commands */
1529 printf("Unknown command \'%s\'\n", cmd);
1530 goto generic_help;
1531 }
1532 }
1533
1534 free(args);
1535 return 0;
1536}
1537
1538COMMAND commands[] = {
1539 {"help", cmd_help, NULL, "Display commands description"},
1540 {"add", cmd_add, cmd_add_help, "Add a new model from a specific file"},
1541 {"load", cmd_load, cmd_load_help, "Load a new model from the searchdirs"},
1542 {"print", cmd_print, cmd_print_help, "Print a model"},
Radek Krejcied5acc52019-04-25 15:57:04 +02001543 {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
Radek Krejcie7b95092019-05-15 11:03:07 +02001544#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001545 {"xpath", cmd_xpath, cmd_xpath_help, "Get data nodes satisfying an XPath expression"},
1546 {"list", cmd_list, cmd_list_help, "List all the loaded models"},
1547#endif
1548 {"feature", cmd_feature, cmd_feature_help, "Print/enable/disable all/specific features of models"},
1549 {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for models"},
1550 {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded models"},
1551 {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
1552#ifndef NDEBUG
1553 {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
1554#endif
1555 {"quit", cmd_quit, NULL, "Quit the program"},
1556 /* synonyms for previous commands */
1557 {"?", cmd_help, NULL, "Display commands description"},
1558 {"exit", cmd_quit, NULL, "Quit the program"},
1559 {NULL, NULL, NULL, NULL}
1560};