blob: 90a1480b1f45f9d2fb0837959aeb2704cd43b698 [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
15#include "config.h"
16
17#include <string.h>
18#include <stdio.h>
19#include <errno.h>
20#include <ctype.h>
21#include <assert.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <getopt.h>
26#include <libgen.h>
27
28#include "commands.h"
29#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{
61 printf("print [-f (yang | yin | tree [<tree-options>] | info [-P <info-path>] | jsons)] [-o <output-file>]"
62 " <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");
71 printf("\tinfo-path:\t<schema-path> | typedef[<schema-path>]/<typedef-name> |\n");
72 printf("\t \t| identity/<identity-name> | feature/<feature-name> |\n");
73 printf("\t \t| grouping[<schema-path>]/<grouping-name> |\n");
74 printf("\t \t| type/<schema-path-leaf-or-leaflist>\n");
75 printf("\n");
76 printf("\tschema-path:\t( /<module-name>:<node-identifier> )+\n");
77}
78
79void
80cmd_data_help(void)
81{
82 printf("data [-(-s)trict] [-t TYPE] [-d DEFAULTS] [-o <output-file>] [-f (xml | json | lyb)] [-r <running-file-name>]\n");
83 printf(" <data-file-name> [<RPC/action-data-file-name> | <yang-data name>]\n\n");
84 printf("Accepted TYPEs:\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("\tdata - LYD_OPT_DATA (default value) - complete datastore including status data.\n");
88 printf("\tconfig - LYD_OPT_CONFIG - complete configuration datastore.\n");
89 printf("\tget - LYD_OPT_GET - <get> operation result.\n");
90 printf("\tgetconfig - LYD_OPT_GETCONFIG - <get-config> operation result.\n");
91 printf("\tedit - LYD_OPT_EDIT - <edit-config>'s data (content of its <config> element).\n");
92 printf("\trpc - LYD_OPT_RPC - NETCONF RPC message.\n");
93 printf("\trpcreply - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
94 printf("\tnotif - LYD_OPT_NOTIF - NETCONF Notification message.\n");
95 printf("\tyangdata - LYD_OPT_DATA_TEMPLATE - yang-data extension (last parameter mandatory in this case)\n\n");
96 printf("Accepted DEFAULTS:\n");
97 printf("\tall - add missing default nodes\n");
98 printf("\tall-tagged - add missing default nodes and mark all the default nodes with the attribute.\n");
99 printf("\ttrim - remove all nodes with a default value\n");
100 printf("\timplicit-tagged - add missing nodes and mark them with the attribute\n\n");
101 printf("Option -r:\n");
102 printf("\tOptional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the file contains running\n");
103 printf("\tconfiguration datastore data referenced from the RPC/Notification. Note that the file is\n");
104 printf("\tvalidated as 'data' TYPE. Special value '!' can be used as argument to ignore the\n");
105 printf("\texternal references.\n\n");
106 printf("\tIf an XPath expression (when/must) needs access to configuration data, you can provide\n");
107 printf("\tthem in a file, which will be parsed as 'data' TYPE.\n\n");
108}
109
110void
111cmd_xpath_help(void)
112{
113 printf("xpath [-t TYPE] [-x <additional-tree-file-name>] -e <XPath-expression>\n"
114 " <XML-data-file-name> [<JSON-rpc/action-schema-nodeid>]\n");
115 printf("Accepted TYPEs:\n");
116 printf("\tauto - resolve data type (one of the following) automatically (as pyang does),\n");
117 printf("\t this option is applicable only in case of XML input data.\n");
118 printf("\tconfig - LYD_OPT_CONFIG\n");
119 printf("\tget - LYD_OPT_GET\n");
120 printf("\tgetconfig - LYD_OPT_GETCONFIG\n");
121 printf("\tedit - LYD_OPT_EDIT\n");
122 printf("\trpc - LYD_OPT_RPC\n");
123 printf("\trpcreply - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
124 printf("\tnotif - LYD_OPT_NOTIF\n\n");
125 printf("Option -x:\n");
126 printf("\tIf RPC/action/notification/RPC reply (for TYPEs 'rpc', 'rpcreply', and 'notif') includes\n");
127 printf("\tan XPath expression (when/must) that needs access to the configuration data, you can provide\n");
128 printf("\tthem in a file, which will be parsed as 'config'.\n");
129}
130
131void
132cmd_list_help(void)
133{
134 printf("list [-f (xml | json)]\n\n");
135 printf("\tBasic list output (no -f): i - imported module, I - implemented module\n");
136}
137
138void
139cmd_feature_help(void)
140{
141 printf("feature [ -(-e)nable | -(-d)isable (* | <feature-name>[,<feature-name> ...]) ] <model-name>[@<revision>]\n");
142}
143
144void
145cmd_searchpath_help(void)
146{
147 printf("searchpath [<model-dir-path> | --clear]\n\n");
148 printf("\tThey are used to search for imports and includes of a model.\n");
149 printf("\tThe \"load\" command uses these directories to find models directly.\n");
150}
151
152void
153cmd_verb_help(void)
154{
155 printf("verb (error/0 | warning/1 | verbose/2 | debug/3)\n");
156}
157
158#ifndef NDEBUG
159
160void
161cmd_debug_help(void)
162{
163 printf("debug (dict | yang | yin | xpath | diff)+\n");
164}
165
166#endif
167
168LYS_INFORMAT
169get_schema_format(const char *path)
170{
171 char *ptr;
172
173 if ((ptr = strrchr(path, '.')) != NULL) {
174 ++ptr;
175 if (!strcmp(ptr, "yang")) {
176 return LYS_IN_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200177 } else if (!strcmp(ptr, "yin")) {
178 return LYS_IN_YIN;
Radek Krejcied5acc52019-04-25 15:57:04 +0200179 } else {
180 fprintf(stderr, "Input file in an unknown format \"%s\".\n", ptr);
181 return LYS_IN_UNKNOWN;
182 }
183 } else {
184 fprintf(stdout, "Input file \"%s\" without extension - unknown format.\n", path);
185 return LYS_IN_UNKNOWN;
186 }
187}
188
189int
190cmd_add(const char *arg)
191{
192 int path_len, ret = 1, index = 0;
193 char *path, *dir, *s, *arg_ptr;
194 const char * const *searchpaths;
195 const struct lys_module *model;
196 LYS_INFORMAT format = LYS_IN_UNKNOWN;
197
198 if (strlen(arg) < 5) {
199 cmd_add_help();
200 return 1;
201 }
202
203 arg_ptr = strdup(arg + 3 /* ignore "add" */);
204
205 for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
206 if (s[2] == '\0' || s[2] == ' ') {
Radek Krejci3fa46b62019-09-11 10:47:30 +0200207 ly_ctx_set_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200208 s[0] = s[1] = ' ';
209 }
210 }
211 s = arg_ptr;
212
213 while (arg_ptr[0] == ' ') {
214 ++arg_ptr;
215 }
216 if (strchr(arg_ptr, ' ')) {
217 path_len = strchr(arg_ptr, ' ') - arg_ptr;
218 } else {
219 path_len = strlen(arg_ptr);
220 }
221 path = strndup(arg_ptr, path_len);
222
223 searchpaths = ly_ctx_get_searchdirs(ctx);
224 if (searchpaths) {
225 for (index = 0; searchpaths[index]; index++);
226 }
227
228 while (path) {
229 format = get_schema_format(path);
230 if (format == LYS_IN_UNKNOWN) {
231 free(path);
232 goto cleanup;
233 }
234
235 dir = strdup(path);
236 ly_ctx_set_searchdir(ctx, dirname(dir));
237 model = lys_parse_path(ctx, path, format);
238 ly_ctx_unset_searchdir(ctx, index);
239 free(path);
240 free(dir);
241
242 if (!model) {
243 /* libyang printed the error messages */
244 goto cleanup;
245 }
246
247 /* next model */
248 arg_ptr += path_len;
249 while (arg_ptr[0] == ' ') {
250 ++arg_ptr;
251 }
252 if (strchr(arg_ptr, ' ')) {
253 path_len = strchr(arg_ptr, ' ') - arg_ptr;
254 } else {
255 path_len = strlen(arg_ptr);
256 }
257
258 if (path_len) {
259 path = strndup(arg_ptr, path_len);
260 } else {
261 path = NULL;
262 }
263 }
264 if (format == LYS_IN_UNKNOWN) {
265 /* no schema on input */
266 cmd_add_help();
267 goto cleanup;
268 }
269 ret = 0;
270
271cleanup:
272 free(s);
Radek Krejci3fa46b62019-09-11 10:47:30 +0200273 ly_ctx_unset_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200274
275 return ret;
276}
277
278int
279cmd_load(const char *arg)
280{
281 int name_len, ret = 1;
282 char *name, *s, *arg_ptr;
283 const struct lys_module *model;
284
285 if (strlen(arg) < 6) {
286 cmd_load_help();
287 return 1;
288 }
289
290 arg_ptr = strdup(arg + 4 /* ignore "load" */);
291
292 for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
293 if (s[2] == '\0' || s[2] == ' ') {
Radek Krejci3fa46b62019-09-11 10:47:30 +0200294 ly_ctx_set_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200295 s[0] = s[1] = ' ';
296 }
297 }
298 s = arg_ptr;
299
300 while (arg_ptr[0] == ' ') {
301 ++arg_ptr;
302 }
303 if (strchr(arg_ptr, ' ')) {
304 name_len = strchr(arg_ptr, ' ') - arg_ptr;
305 } else {
306 name_len = strlen(arg_ptr);
307 }
308 name = strndup(arg_ptr, name_len);
309
310 while (name) {
311 model = ly_ctx_load_module(ctx, name, NULL);
312 free(name);
313 if (!model) {
314 /* libyang printed the error messages */
315 goto cleanup;
316 }
317
318 /* next model */
319 arg_ptr += name_len;
320 while (arg_ptr[0] == ' ') {
321 ++arg_ptr;
322 }
323 if (strchr(arg_ptr, ' ')) {
324 name_len = strchr(arg_ptr, ' ') - arg_ptr;
325 } else {
326 name_len = strlen(arg_ptr);
327 }
328
329 if (name_len) {
330 name = strndup(arg_ptr, name_len);
331 } else {
332 name = NULL;
333 }
334 }
335 ret = 0;
336
337cleanup:
338 free(s);
Radek Krejci3fa46b62019-09-11 10:47:30 +0200339 ly_ctx_unset_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200340
341 return ret;
342}
343
344int
345cmd_print(const char *arg)
346{
Radek Krejci693262f2019-04-29 15:23:20 +0200347 int c, argc, option_index, ret = 1, tree_ll = 0, tree_opts = 0, compiled = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200348 char **argv = NULL, *ptr, *model_name, *revision;
349 const char *out_path = NULL;
350 const struct lys_module *module;
351 LYS_OUTFORMAT format = LYS_OUT_TREE;
352 FILE *output = stdout;
353 static struct option long_options[] = {
Radek Krejci693262f2019-04-29 15:23:20 +0200354 {"compiled", no_argument, 0, 'c'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200355 {"help", no_argument, 0, 'h'},
356 {"format", required_argument, 0, 'f'},
357 {"output", required_argument, 0, 'o'},
358#if 0
359 {"tree-print-groupings", no_argument, 0, 'g'},
360 {"tree-print-uses", no_argument, 0, 'u'},
361 {"tree-no-leafref-target", no_argument, 0, 'n'},
362 {"tree-path", required_argument, 0, 'P'},
363 {"info-path", required_argument, 0, 'P'},
364 {"tree-line-length", required_argument, 0, 'L'},
365#endif
366 {NULL, 0, 0, 0}
367 };
368 void *rlcd;
369
370 argc = 1;
371 argv = malloc(2*sizeof *argv);
372 *argv = strdup(arg);
373 ptr = strtok(*argv, " ");
374 while ((ptr = strtok(NULL, " "))) {
375 rlcd = realloc(argv, (argc+2)*sizeof *argv);
376 if (!rlcd) {
377 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
378 goto cleanup;
379 }
380 argv = rlcd;
381 argv[argc++] = ptr;
382 }
383 argv[argc] = NULL;
384
385 optind = 0;
386 while (1) {
387 option_index = 0;
Radek Krejci693262f2019-04-29 15:23:20 +0200388 c = getopt_long(argc, argv, "chf:go:guP:L:", long_options, &option_index);
Radek Krejcied5acc52019-04-25 15:57:04 +0200389 if (c == -1) {
390 break;
391 }
392
393 switch (c) {
Radek Krejci693262f2019-04-29 15:23:20 +0200394 case 'c':
395 compiled = 1;
396 break;
Radek Krejcied5acc52019-04-25 15:57:04 +0200397 case 'h':
398 cmd_print_help();
399 ret = 0;
400 goto cleanup;
401 case 'f':
402 if (!strcmp(optarg, "yang")) {
403 format = LYS_OUT_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200404 } else if (!strcmp(optarg, "yin")) {
405 format = LYS_OUT_YIN;
FredGand944bdc2019-11-05 21:57:07 +0800406#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200407 } else if (!strcmp(optarg, "tree")) {
408 format = LYS_OUT_TREE;
409 } else if (!strcmp(optarg, "tree-rfc")) {
410 format = LYS_OUT_TREE;
411 tree_opts |= LYS_OUTOPT_TREE_RFC;
412 } else if (!strcmp(optarg, "info")) {
413 format = LYS_OUT_INFO;
414 } else if (!strcmp(optarg, "jsons")) {
415 format = LYS_OUT_JSON;
416#endif
417 } else {
418 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
419 goto cleanup;
420 }
421 break;
422 case 'o':
423 if (out_path) {
424 fprintf(stderr, "Output specified twice.\n");
425 goto cleanup;
426 }
427 out_path = optarg;
428 break;
429#if 0
430 case 'g':
431 tree_opts |= LYS_OUTOPT_TREE_GROUPING;
432 break;
433 case 'u':
434 tree_opts |= LYS_OUTOPT_TREE_USES;
435 break;
436 case 'n':
437 tree_opts |= LYS_OUTOPT_TREE_NO_LEAFREF;
438 break;
439 case 'P':
440 target_path = optarg;
441 break;
442 case 'L':
443 tree_ll = atoi(optarg);
444 break;
445#endif
446 case '?':
447 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
448 goto cleanup;
449 }
450 }
451
452 /* file name */
453 if (optind == argc) {
454 fprintf(stderr, "Missing the module name.\n");
455 goto cleanup;
456 }
457
Radek Krejci693262f2019-04-29 15:23:20 +0200458 /* compiled format */
459 if (compiled) {
FredGand944bdc2019-11-05 21:57:07 +0800460 if (format == LYS_OUT_YANG) {
461 format = LYS_OUT_YANG_COMPILED;
462 } else {
463 fprintf(stderr, "warning: --compiled option takes effect only in case of printing schemas in YANG format.\n");
464 }
Radek Krejci693262f2019-04-29 15:23:20 +0200465 }
Radek Krejci77954e82019-04-30 13:51:29 +0200466#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200467 /* tree fromat with or without gropings */
468 if ((tree_opts || tree_ll) && format != LYS_OUT_TREE) {
469 fprintf(stderr, "--tree options take effect only in case of the tree output format.\n");
470 }
Radek Krejci77954e82019-04-30 13:51:29 +0200471#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200472 /* 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 }
486#if 0
487 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 }
491#endif
492
493 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;
500 }
501
502 if (out_path) {
503 output = fopen(out_path, "w");
504 if (!output) {
505 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
506 goto cleanup;
507 }
508 }
509
510 ret = lys_print_file(output, module, format, tree_ll, tree_opts);
511 if (format == LYS_OUT_JSON) {
512 fputs("\n", output);
513 }
514
515cleanup:
516 free(*argv);
517 free(argv);
518
519 if (output && (output != stdout)) {
520 fclose(output);
521 }
522
523 return ret;
524}
Radek Krejcie7b95092019-05-15 11:03:07 +0200525
Radek Krejcied5acc52019-04-25 15:57:04 +0200526static LYD_FORMAT
527detect_data_format(char *filepath)
528{
529 size_t len;
530
531 /* detect input format according to file suffix */
532 len = strlen(filepath);
533 for (; isspace(filepath[len - 1]); len--, filepath[len] = '\0'); /* remove trailing whitespaces */
534 if (len >= 5 && !strcmp(&filepath[len - 4], ".xml")) {
535 return LYD_XML;
Radek Krejcie7b95092019-05-15 11:03:07 +0200536#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200537 } else if (len >= 6 && !strcmp(&filepath[len - 5], ".json")) {
538 return LYD_JSON;
539 } else if (len >= 5 && !strcmp(&filepath[len - 4], ".lyb")) {
540 return LYD_LYB;
Radek Krejcie7b95092019-05-15 11:03:07 +0200541#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200542 } else {
543 return LYD_UNKNOWN;
544 }
545}
546
547static int
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200548parse_data(char *filepath, int *options, const struct lyd_node **trees, const char *rpc_act_file,
Radek Krejcied5acc52019-04-25 15:57:04 +0200549 struct lyd_node **result)
550{
551 LYD_FORMAT informat = LYD_UNKNOWN;
Radek Krejcied5acc52019-04-25 15:57:04 +0200552 struct lyd_node *data = NULL, *rpc_act = NULL;
553 int opts = *options;
554
555 /* detect input format according to file suffix */
556 informat = detect_data_format(filepath);
557 if (informat == LYD_UNKNOWN) {
558 fprintf(stderr, "Unable to resolve format of the input file, please add \".xml\", \".json\", or \".lyb\" suffix.\n");
559 return EXIT_FAILURE;
560 }
561
Radek Krejcie7b95092019-05-15 11:03:07 +0200562 ly_err_clean(ctx, NULL);
Radek Krejcied5acc52019-04-25 15:57:04 +0200563
Radek Krejcie7b95092019-05-15 11:03:07 +0200564#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200565 if ((opts & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
566 /* automatically detect data type from the data top level */
567 if (informat != LYD_XML) {
568 fprintf(stderr, "Only XML data can be automatically explored.\n");
569 return EXIT_FAILURE;
570 }
571
572 xml = lyxml_parse_path(ctx, filepath, 0);
573 if (!xml) {
574 fprintf(stderr, "Failed to parse XML data for automatic type detection.\n");
575 return EXIT_FAILURE;
576 }
577
578 /* NOTE: namespace is ignored to simplify usage of this feature */
579
580 if (!strcmp(xml->name, "data")) {
581 fprintf(stdout, "Parsing %s as complete datastore.\n", filepath);
582 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_ADD_YANGLIB;
583 } else if (!strcmp(xml->name, "config")) {
584 fprintf(stdout, "Parsing %s as config data.\n", filepath);
585 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
586 } else if (!strcmp(xml->name, "get-reply")) {
587 fprintf(stdout, "Parsing %s as <get> reply data.\n", filepath);
588 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
589 } else if (!strcmp(xml->name, "get-config-reply")) {
590 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", filepath);
591 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
592 } else if (!strcmp(xml->name, "edit-config")) {
593 fprintf(stdout, "Parsing %s as <edit-config> data.\n", filepath);
594 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
595 } else if (!strcmp(xml->name, "rpc")) {
596 fprintf(stdout, "Parsing %s as <rpc> data.\n", filepath);
597 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
598 } else if (!strcmp(xml->name, "rpc-reply")) {
599 if (!rpc_act_file) {
600 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
601 lyxml_free(ctx, xml);
602 return EXIT_FAILURE;
603 }
604 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", filepath);
605 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
606 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, val_tree);
607 if (!rpc_act) {
608 fprintf(stderr, "Failed to parse RPC/action.\n");
609 lyxml_free(ctx, xml);
610 return EXIT_FAILURE;
611 }
612 } else if (!strcmp(xml->name, "notification")) {
613 fprintf(stdout, "Parsing %s as <notification> data.\n", filepath);
614 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
615 } else if (!strcmp(xml->name, "yang-data")) {
616 fprintf(stdout, "Parsing %s as <yang-data> data.\n", filepath);
617 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
618 if (!rpc_act_file) {
619 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
620 lyxml_free(ctx, xml);
621 return EXIT_FAILURE;
622 }
623 } else {
624 fprintf(stderr, "Invalid top-level element for automatic data type recognition.\n");
625 lyxml_free(ctx, xml);
626 return EXIT_FAILURE;
627 }
628
629 if (opts & LYD_OPT_RPCREPLY) {
630 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act, val_tree);
631 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
632 data = lyd_parse_xml(ctx, &xml->child, opts, val_tree);
633 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
634 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act_file);
635 } else {
636 data = lyd_parse_xml(ctx, &xml->child, opts);
637 }
638 lyxml_free(ctx, xml);
639 } else {
Radek Krejcie7b95092019-05-15 11:03:07 +0200640#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200641 if (opts & LYD_OPT_RPCREPLY) {
642 if (!rpc_act_file) {
643 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
644 return EXIT_FAILURE;
645 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200646 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200647 if (!rpc_act) {
648 fprintf(stderr, "Failed to parse RPC/action.\n");
649 return EXIT_FAILURE;
650 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200651 if (trees) {
652 const struct lyd_node **trees_new;
653 unsigned int u;
654 trees_new = lyd_trees_new(1, rpc_act);
655
656 LY_ARRAY_FOR(trees, u) {
657 trees_new = lyd_trees_add(trees_new, trees[u]);
658 }
659 lyd_trees_free(trees, 0);
660 trees = trees_new;
661 } else {
662 trees = lyd_trees_new(1, rpc_act);
663 }
664 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200665 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200666 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
667#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200668 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
669 if (!rpc_act_file) {
670 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
671 return EXIT_FAILURE;
672 }
673 data = lyd_parse_path(ctx, filepath, informat, opts, rpc_act_file);
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200674#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200675 } else {
676 if (!(opts & LYD_OPT_TYPEMASK)) {
677 /* automatically add yang-library data */
678 opts |= LYD_OPT_DATA_ADD_YANGLIB;
679 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200680 data = lyd_parse_path(ctx, filepath, informat, opts, NULL);
Radek Krejcied5acc52019-04-25 15:57:04 +0200681 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200682#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200683 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200684#endif
685 lyd_free_all(rpc_act);
Radek Krejcied5acc52019-04-25 15:57:04 +0200686
Radek Krejcie7b95092019-05-15 11:03:07 +0200687 if (ly_err_first(ctx)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200688 fprintf(stderr, "Failed to parse data.\n");
Radek Krejcie7b95092019-05-15 11:03:07 +0200689 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200690 return EXIT_FAILURE;
691 }
692
693 *result = data;
694 *options = opts;
695 return EXIT_SUCCESS;
696}
697
698int
699cmd_data(const char *arg)
700{
701 int c, argc, option_index, ret = 1;
702 int options = 0, printopt = 0;
703 char **argv = NULL, *ptr;
704 const char *out_path = NULL;
705 struct lyd_node *data = NULL, *val_tree = NULL;
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200706 const struct lyd_node **trees = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200707 LYD_FORMAT outformat = LYD_UNKNOWN;
708 FILE *output = stdout;
709 static struct option long_options[] = {
710 {"defaults", required_argument, 0, 'd'},
711 {"help", no_argument, 0, 'h'},
712 {"format", required_argument, 0, 'f'},
713 {"option", required_argument, 0, 't'},
714 {"output", required_argument, 0, 'o'},
715 {"running", required_argument, 0, 'r'},
716 {"strict", no_argument, 0, 's'},
717 {NULL, 0, 0, 0}
718 };
719 void *rlcd;
720
721 argc = 1;
722 argv = malloc(2*sizeof *argv);
723 *argv = strdup(arg);
724 ptr = strtok(*argv, " ");
725 while ((ptr = strtok(NULL, " "))) {
726 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
727 if (!rlcd) {
728 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
729 goto cleanup;
730 }
731 argv = rlcd;
732 argv[argc++] = ptr;
733 }
734 argv[argc] = NULL;
735
736 optind = 0;
737 while (1) {
738 option_index = 0;
739 c = getopt_long(argc, argv, "d:hf:o:st:r:", long_options, &option_index);
740 if (c == -1) {
741 break;
742 }
743
744 switch (c) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200745#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200746 case 'd':
747 if (!strcmp(optarg, "all")) {
748 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL;
749 } else if (!strcmp(optarg, "all-tagged")) {
750 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
751 } else if (!strcmp(optarg, "trim")) {
752 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_TRIM;
753 } else if (!strcmp(optarg, "implicit-tagged")) {
754 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
755 }
756 break;
Radek Krejcie7b95092019-05-15 11:03:07 +0200757#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200758 case 'h':
759 cmd_data_help();
760 ret = 0;
761 goto cleanup;
762 case 'f':
763 if (!strcmp(optarg, "xml")) {
764 outformat = LYD_XML;
Radek Krejcie7b95092019-05-15 11:03:07 +0200765#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200766 } else if (!strcmp(optarg, "json")) {
767 outformat = LYD_JSON;
768 } else if (!strcmp(optarg, "lyb")) {
769 outformat = LYD_LYB;
Radek Krejcie7b95092019-05-15 11:03:07 +0200770#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200771 } else {
772 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
773 goto cleanup;
774 }
775 break;
776 case 'o':
777 if (out_path) {
778 fprintf(stderr, "Output specified twice.\n");
779 goto cleanup;
780 }
781 out_path = optarg;
782 break;
783 case 'r':
Radek Krejcied5acc52019-04-25 15:57:04 +0200784 if (optarg[0] == '!') {
785 /* ignore extenral dependencies to the running datastore */
786 options |= LYD_OPT_NOEXTDEPS;
787 } else {
788 /* external file with the running datastore */
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200789 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_DATA_NO_YANGLIB, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200790 if (!val_tree) {
791 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
792 goto cleanup;
793 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200794 if (!trees) {
795 trees = lyd_trees_new(1, val_tree);
796 } else {
797 trees = lyd_trees_add(trees, val_tree);
798 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200799 }
800 break;
801 case 's':
802 options |= LYD_OPT_STRICT;
803 options |= LYD_OPT_OBSOLETE;
804 break;
805 case 't':
806 if (!strcmp(optarg, "auto")) {
807 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
808 } else if (!strcmp(optarg, "data")) {
809 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA;
810 } else if (!strcmp(optarg, "config")) {
811 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
812 } else if (!strcmp(optarg, "get")) {
813 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
814 } else if (!strcmp(optarg, "getconfig")) {
815 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
816 } else if (!strcmp(optarg, "edit")) {
817 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
818 } else if (!strcmp(optarg, "rpc")) {
819 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
820 } else if (!strcmp(optarg, "rpcreply")) {
821 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
822 } else if (!strcmp(optarg, "notif")) {
823 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
824 } else if (!strcmp(optarg, "yangdata")) {
825 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
826 } else {
827 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
828 cmd_data_help();
829 goto cleanup;
830 }
831 break;
832 case '?':
833 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
834 goto cleanup;
835 }
836 }
837
838 /* file name */
839 if (optind == argc) {
840 fprintf(stderr, "Missing the data file name.\n");
841 goto cleanup;
842 }
843
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200844 if (parse_data(argv[optind], &options, trees, argv[optind + 1], &data)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200845 goto cleanup;
846 }
847
848 if (out_path) {
849 output = fopen(out_path, "w");
850 if (!output) {
851 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
852 goto cleanup;
853 }
854 }
855
856 if (outformat != LYD_UNKNOWN) {
857 if (options & LYD_OPT_RPCREPLY) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200858 lyd_print_file(output, lyd_node_children(data), outformat, LYDP_WITHSIBLINGS | LYDP_FORMAT | printopt);
Radek Krejcied5acc52019-04-25 15:57:04 +0200859 } else {
Radek Krejcie7b95092019-05-15 11:03:07 +0200860 lyd_print_file(output, data, outformat, LYDP_WITHSIBLINGS | LYDP_FORMAT | printopt);
Radek Krejcied5acc52019-04-25 15:57:04 +0200861 }
862 }
863
864 ret = 0;
865
866cleanup:
867 free(*argv);
868 free(argv);
869
870 if (output && (output != stdout)) {
871 fclose(output);
872 }
873
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200874 lyd_trees_free(trees, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200875 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200876
877 return ret;
878}
Radek Krejcie7b95092019-05-15 11:03:07 +0200879#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200880int
881cmd_xpath(const char *arg)
882{
883 int c, argc, option_index, ret = 1, long_str;
884 char **argv = NULL, *ptr, *expr = NULL;
885 unsigned int i, j;
886 int options = 0;
887 struct lyd_node *data = NULL, *node, *val_tree = NULL;
888 struct lyd_node_leaf_list *key;
889 struct ly_set *set;
890 static struct option long_options[] = {
891 {"help", no_argument, 0, 'h'},
892 {"expr", required_argument, 0, 'e'},
893 {NULL, 0, 0, 0}
894 };
895 void *rlcd;
896
897 long_str = 0;
898 argc = 1;
899 argv = malloc(2 * sizeof *argv);
900 *argv = strdup(arg);
901 ptr = strtok(*argv, " ");
902 while ((ptr = strtok(NULL, " "))) {
903 if (long_str) {
904 ptr[-1] = ' ';
905 if (ptr[strlen(ptr) - 1] == long_str) {
906 long_str = 0;
907 ptr[strlen(ptr) - 1] = '\0';
908 }
909 } else {
910 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
911 if (!rlcd) {
912 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
913 goto cleanup;
914 }
915 argv = rlcd;
916 argv[argc] = ptr;
917 if (ptr[0] == '"') {
918 long_str = '"';
919 ++argv[argc];
920 }
921 if (ptr[0] == '\'') {
922 long_str = '\'';
923 ++argv[argc];
924 }
925 if (ptr[strlen(ptr) - 1] == long_str) {
926 long_str = 0;
927 ptr[strlen(ptr) - 1] = '\0';
928 }
929 ++argc;
930 }
931 }
932 argv[argc] = NULL;
933
934 optind = 0;
935 while (1) {
936 option_index = 0;
937 c = getopt_long(argc, argv, "he:t:x:", long_options, &option_index);
938 if (c == -1) {
939 break;
940 }
941
942 switch (c) {
943 case 'h':
944 cmd_xpath_help();
945 ret = 0;
946 goto cleanup;
947 case 'e':
948 expr = optarg;
949 break;
950 case 't':
951 if (!strcmp(optarg, "auto")) {
952 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
953 } else if (!strcmp(optarg, "config")) {
954 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
955 } else if (!strcmp(optarg, "get")) {
956 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
957 } else if (!strcmp(optarg, "getconfig")) {
958 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
959 } else if (!strcmp(optarg, "edit")) {
960 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
961 } else if (!strcmp(optarg, "rpc")) {
962 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
963 } else if (!strcmp(optarg, "rpcreply")) {
964 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
965 } else if (!strcmp(optarg, "notif")) {
966 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
967 } else if (!strcmp(optarg, "yangdata")) {
968 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
969 } else {
970 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
971 cmd_data_help();
972 goto cleanup;
973 }
974 break;
975 case 'x':
976 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_CONFIG);
977 if (!val_tree) {
978 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
979 goto cleanup;
980 }
981 break;
982 case '?':
983 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
984 goto cleanup;
985 }
986 }
987
988 if (optind == argc) {
989 fprintf(stderr, "Missing the file with data.\n");
990 goto cleanup;
991 }
992
993 if (!expr) {
994 fprintf(stderr, "Missing the XPath expression.\n");
995 goto cleanup;
996 }
997
998 if (parse_data(argv[optind], &options, val_tree, argv[optind + 1], &data)) {
999 goto cleanup;
1000 }
1001
1002 if (!(set = lyd_find_path(data, expr))) {
1003 goto cleanup;
1004 }
1005
1006 /* print result */
1007 printf("Result:\n");
1008 if (!set->number) {
1009 printf("\tEmpty\n");
1010 } else {
1011 for (i = 0; i < set->number; ++i) {
1012 node = set->set.d[i];
1013 switch (node->schema->nodetype) {
1014 case LYS_CONTAINER:
1015 printf("\tContainer ");
1016 break;
1017 case LYS_LEAF:
1018 printf("\tLeaf ");
1019 break;
1020 case LYS_LEAFLIST:
1021 printf("\tLeaflist ");
1022 break;
1023 case LYS_LIST:
1024 printf("\tList ");
1025 break;
1026 case LYS_ANYXML:
1027 printf("\tAnyxml ");
1028 break;
1029 case LYS_ANYDATA:
1030 printf("\tAnydata ");
1031 break;
1032 default:
1033 printf("\tUnknown ");
1034 break;
1035 }
1036 printf("\"%s\"", node->schema->name);
1037 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
1038 printf(" (val: %s)", ((struct lyd_node_leaf_list *)node)->value_str);
1039 } else if (node->schema->nodetype == LYS_LIST) {
1040 key = (struct lyd_node_leaf_list *)node->child;
1041 printf(" (");
1042 for (j = 0; j < ((struct lys_node_list *)node->schema)->keys_size; ++j) {
1043 if (j) {
1044 printf(" ");
1045 }
1046 printf("\"%s\": %s", key->schema->name, key->value_str);
1047 key = (struct lyd_node_leaf_list *)key->next;
1048 }
1049 printf(")");
1050 }
1051 printf("\n");
1052 }
1053 }
1054 printf("\n");
1055
1056 ly_set_free(set);
1057 ret = 0;
1058
1059cleanup:
1060 free(*argv);
1061 free(argv);
1062
1063 lyd_free_withsiblings(data);
1064
1065 return ret;
1066}
1067
1068int
1069print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
1070{
1071 struct lyd_node *ylib;
1072 uint32_t idx = 0, has_modules = 0;
1073 uint8_t u;
1074 const struct lys_module *mod;
1075
1076 if (outformat != LYD_UNKNOWN) {
1077 ylib = ly_ctx_info(ctx);
1078 if (!ylib) {
1079 fprintf(stderr, "Getting context info (ietf-yang-library data) failed.\n");
1080 return 1;
1081 }
1082
1083 lyd_print_file(out, ylib, outformat, LYP_WITHSIBLINGS | LYP_FORMAT);
1084 lyd_free_withsiblings(ylib);
1085 return 0;
1086 }
1087
1088 /* iterate schemas in context and provide just the basic info */
1089 fprintf(out, "List of the loaded models:\n");
1090 while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
1091 has_modules++;
1092
1093 /* conformance print */
1094 if (mod->implemented) {
1095 fprintf(out, "\tI");
1096 } else {
1097 fprintf(out, "\ti");
1098 }
1099
1100 /* module print */
1101 fprintf(out, " %s", mod->name);
1102 if (mod->rev_size) {
1103 fprintf(out, "@%s", mod->rev[0].date);
1104 }
1105
1106 /* submodules print */
1107 if (mod->inc_size) {
1108 fprintf(out, " (");
1109 for (u = 0; u < mod->inc_size; u++) {
1110 fprintf(out, "%s%s", !u ? "" : ",", mod->inc[u].submodule->name);
1111 if (mod->inc[u].submodule->rev_size) {
1112 fprintf(out, "@%s", mod->inc[u].submodule->rev[0].date);
1113 }
1114 }
1115 fprintf(out, ")");
1116 }
1117
1118 /* finish the line */
1119 fprintf(out, "\n");
1120 }
1121
1122 if (!has_modules) {
1123 fprintf(out, "\t(none)\n");
1124 }
1125
1126 return 0;
1127}
1128
1129int
1130cmd_list(const char *arg)
1131{
1132 char **argv = NULL, *ptr;
1133 int c, argc, option_index;
1134 LYD_FORMAT outformat = LYD_UNKNOWN;
1135 static struct option long_options[] = {
1136 {"help", no_argument, 0, 'h'},
1137 {"format", required_argument, 0, 'f'},
1138 {NULL, 0, 0, 0}
1139 };
1140 void *rlcd;
1141
1142 argc = 1;
1143 argv = malloc(2*sizeof *argv);
1144 *argv = strdup(arg);
1145 ptr = strtok(*argv, " ");
1146 while ((ptr = strtok(NULL, " "))) {
1147 rlcd = realloc(argv, (argc+2)*sizeof *argv);
1148 if (!rlcd) {
1149 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1150 goto error;
1151 }
1152 argv = rlcd;
1153 argv[argc++] = ptr;
1154 }
1155 argv[argc] = NULL;
1156
1157 optind = 0;
1158 while (1) {
1159 option_index = 0;
1160 c = getopt_long(argc, argv, "hf:", long_options, &option_index);
1161 if (c == -1) {
1162 break;
1163 }
1164
1165 switch (c) {
1166 case 'h':
1167 cmd_data_help();
1168 free(*argv);
1169 free(argv);
1170 return 0;
1171 case 'f':
1172 if (!strcmp(optarg, "xml")) {
1173 outformat = LYD_XML;
1174 } else if (!strcmp(optarg, "json")) {
1175 outformat = LYD_JSON;
1176 } else {
1177 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
1178 goto error;
1179 }
1180 break;
1181 case '?':
1182 /* getopt_long() prints message */
1183 goto error;
1184 }
1185 }
1186 if (optind != argc) {
1187 fprintf(stderr, "Unknown parameter \"%s\"\n", argv[optind]);
1188error:
1189 free(*argv);
1190 free(argv);
1191 return 1;
1192 }
1193 free(*argv);
1194 free(argv);
1195
1196 return print_list(stdout, ctx, outformat);
1197}
1198#endif
1199int
1200cmd_feature(const char *arg)
1201{
1202 int c, argc, option_index, ret = 1, task = 0;
1203 char **argv = NULL, *ptr, *model_name, *revision, *feat_names = NULL;
1204 const struct lys_module *module;
1205 static struct option long_options[] = {
1206 {"help", no_argument, 0, 'h'},
1207 {"enable", required_argument, 0, 'e'},
1208 {"disable", required_argument, 0, 'd'},
1209 {NULL, 0, 0, 0}
1210 };
1211 void *rlcd;
1212
1213 argc = 1;
1214 argv = malloc(2*sizeof *argv);
1215 *argv = strdup(arg);
1216 ptr = strtok(*argv, " ");
1217 while ((ptr = strtok(NULL, " "))) {
1218 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
1219 if (!rlcd) {
1220 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1221 goto cleanup;
1222 }
1223 argv = rlcd;
1224 argv[argc++] = ptr;
1225 }
1226 argv[argc] = NULL;
1227
1228 optind = 0;
1229 while (1) {
1230 option_index = 0;
1231 c = getopt_long(argc, argv, "he:d:", long_options, &option_index);
1232 if (c == -1) {
1233 break;
1234 }
1235
1236 switch (c) {
1237 case 'h':
1238 cmd_feature_help();
1239 ret = 0;
1240 goto cleanup;
1241 case 'e':
1242 if (task) {
1243 fprintf(stderr, "Only one of enable or disable can be specified.\n");
1244 goto cleanup;
1245 }
1246 task = 1;
1247 feat_names = optarg;
1248 break;
1249 case 'd':
1250 if (task) {
1251 fprintf(stderr, "Only one of enable, or disable can be specified.\n");
1252 goto cleanup;
1253 }
1254 task = 2;
1255 feat_names = optarg;
1256 break;
1257 case '?':
1258 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
1259 goto cleanup;
1260 }
1261 }
1262
1263 /* module name */
1264 if (optind == argc) {
1265 fprintf(stderr, "Missing the module name.\n");
1266 goto cleanup;
1267 }
1268
1269 revision = NULL;
1270 model_name = argv[optind];
1271 if (strchr(model_name, '@')) {
1272 revision = strchr(model_name, '@');
1273 revision[0] = '\0';
1274 ++revision;
1275 }
1276
1277 module = ly_ctx_get_module(ctx, model_name, revision);
1278#if 0
1279 if (!module) {
1280 /* not a module, try to find it as a submodule */
1281 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
1282 }
1283#endif
1284
1285 if (module == NULL) {
1286 if (revision) {
1287 fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
1288 } else {
1289 fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
1290 }
1291 goto cleanup;
1292 }
1293
1294 if (!task) {
1295 unsigned int len, max_len = 0;
1296 unsigned int u;
1297 struct lysc_feature *features;
1298
1299 printf("%s features:\n", module->name);
1300
1301 if (module->compiled) {
1302 features = module->compiled->features;
1303 } else {
1304 features = module->off_features;
1305 }
1306
1307 /* get the max len */
1308 LY_ARRAY_FOR(features, u) {
1309 len = strlen(features[u].name);
1310 if (len > max_len) {
1311 max_len = len;
1312 }
1313 }
1314
1315 LY_ARRAY_FOR(features, u) {
1316 printf("\t%-*s (%s)\n", max_len, features[u].name, (features[u].flags & LYS_FENABLED) ? "on" : "off");
1317 }
1318 if (!u) {
1319 printf("\t(none)\n");
1320 }
1321 } else {
1322 feat_names = strtok(feat_names, ",");
1323 while (feat_names) {
1324 if (((task == 1) && lys_feature_enable(module, feat_names))
1325 || ((task == 2) && lys_feature_disable(module, feat_names))) {
1326 fprintf(stderr, "Feature \"%s\" not found.\n", feat_names);
1327 ret = 1;
1328 }
1329 feat_names = strtok(NULL, ",");
1330 }
1331 }
1332
1333cleanup:
1334 free(*argv);
1335 free(argv);
1336
1337 return ret;
1338}
1339
1340int
1341cmd_searchpath(const char *arg)
1342{
1343 const char *path;
1344 const char * const *searchpaths;
1345 int index;
1346 struct stat st;
1347
1348 for (path = strchr(arg, ' '); path && (path[0] == ' '); ++path);
1349 if (!path || (path[0] == '\0')) {
1350 searchpaths = ly_ctx_get_searchdirs(ctx);
1351 if (searchpaths) {
1352 for (index = 0; searchpaths[index]; index++) {
1353 fprintf(stdout, "%s\n", searchpaths[index]);
1354 }
1355 }
1356 return 0;
1357 }
1358
1359 if ((!strncmp(path, "-h", 2) && (path[2] == '\0' || path[2] == ' ')) ||
1360 (!strncmp(path, "--help", 6) && (path[6] == '\0' || path[6] == ' '))) {
1361 cmd_searchpath_help();
1362 return 0;
1363 } else if (!strncmp(path, "--clear", 7) && (path[7] == '\0' || path[7] == ' ')) {
1364 ly_ctx_unset_searchdirs(ctx, NULL);
1365 return 0;
1366 }
1367
1368 if (stat(path, &st) == -1) {
1369 fprintf(stderr, "Failed to stat the search path (%s).\n", strerror(errno));
1370 return 1;
1371 }
1372 if (!S_ISDIR(st.st_mode)) {
1373 fprintf(stderr, "\"%s\" is not a directory.\n", path);
1374 return 1;
1375 }
1376
1377 ly_ctx_set_searchdir(ctx, path);
1378
1379 return 0;
1380}
1381
1382int
1383cmd_clear(const char *arg)
1384{
1385 struct ly_ctx *ctx_new;
1386 int options = 0;
1387#if 0
1388 int i;
1389 char *ylpath;
1390 const char * const *searchpaths;
1391 LYD_FORMAT format;
1392
1393 /* get optional yang library file name */
1394 for (i = 5; arg[i] && isspace(arg[i]); i++);
1395 if (arg[i]) {
1396 if (arg[i] == '-' && arg[i + 1] == 'e') {
1397 options = LY_CTX_NOYANGLIBRARY;
1398 goto create_empty;
1399 } else {
1400 ylpath = strdup(&arg[i]);
1401 format = detect_data_format(ylpath);
1402 if (format == LYD_UNKNOWN) {
1403 free(ylpath);
1404 fprintf(stderr, "Unable to resolve format of the yang library file, please add \".xml\" or \".json\" suffix.\n");
1405 goto create_empty;
1406 }
1407 searchpaths = ly_ctx_get_searchdirs(ctx);
1408 ctx_new = ly_ctx_new_ylpath(searchpaths ? searchpaths[0] : NULL, ylpath, format, 0);
1409 free(ylpath);
1410 }
1411 } else {
1412create_empty:
1413#else
Radek Krejci92f5ce92019-09-06 16:25:43 +02001414 (void) arg; /* TODO yang-library support */
Radek Krejcied5acc52019-04-25 15:57:04 +02001415 {
1416#endif
1417 ly_ctx_new(NULL, options, &ctx_new);
1418 }
1419
1420 if (!ctx_new) {
1421 fprintf(stderr, "Failed to create context.\n");
1422 return 1;
1423 }
1424
1425 /* final switch */
1426 ly_ctx_destroy(ctx, NULL);
1427 ctx = ctx_new;
1428
1429 return 0;
1430}
1431
1432int
1433cmd_verb(const char *arg)
1434{
1435 const char *verb;
1436 if (strlen(arg) < 5) {
1437 cmd_verb_help();
1438 return 1;
1439 }
1440
1441 verb = arg + 5;
1442 if (!strcmp(verb, "error") || !strcmp(verb, "0")) {
1443 ly_verb(LY_LLERR);
1444#ifndef NDEBUG
1445 ly_verb_dbg(0);
1446#endif
1447 } else if (!strcmp(verb, "warning") || !strcmp(verb, "1")) {
1448 ly_verb(LY_LLWRN);
1449#ifndef NDEBUG
1450 ly_verb_dbg(0);
1451#endif
1452 } else if (!strcmp(verb, "verbose") || !strcmp(verb, "2")) {
1453 ly_verb(LY_LLVRB);
1454#ifndef NDEBUG
1455 ly_verb_dbg(0);
1456#endif
1457 } else if (!strcmp(verb, "debug") || !strcmp(verb, "3")) {
1458 ly_verb(LY_LLDBG);
1459#ifndef NDEBUG
1460 ly_verb_dbg(LY_LDGDICT | LY_LDGYANG | LY_LDGYIN | LY_LDGXPATH | LY_LDGDIFF);
1461#endif
1462 } else {
1463 fprintf(stderr, "Unknown verbosity \"%s\"\n", verb);
1464 return 1;
1465 }
1466
1467 return 0;
1468}
1469
1470#ifndef NDEBUG
1471
1472int
1473cmd_debug(const char *arg)
1474{
1475 const char *beg, *end;
1476 int grps = 0;
1477 if (strlen(arg) < 6) {
1478 cmd_debug_help();
1479 return 1;
1480 }
1481
1482 end = arg + 6;
1483 while (end[0]) {
1484 for (beg = end; isspace(beg[0]); ++beg);
1485 if (!beg[0]) {
1486 break;
1487 }
1488
1489 for (end = beg; (end[0] && !isspace(end[0])); ++end);
1490
1491 if (!strncmp(beg, "dict", end - beg)) {
1492 grps |= LY_LDGDICT;
1493 } else if (!strncmp(beg, "yang", end - beg)) {
1494 grps |= LY_LDGYANG;
1495 } else if (!strncmp(beg, "yin", end - beg)) {
1496 grps |= LY_LDGYIN;
1497 } else if (!strncmp(beg, "xpath", end - beg)) {
1498 grps |= LY_LDGXPATH;
1499 } else if (!strncmp(beg, "diff", end - beg)) {
1500 grps |= LY_LDGDIFF;
1501 } else {
1502 fprintf(stderr, "Unknown debug group \"%.*s\"\n", (int)(end - beg), beg);
1503 return 1;
1504 }
1505 }
1506 ly_verb_dbg(grps);
1507
1508 return 0;
1509}
1510
1511#endif
1512
1513int
1514cmd_quit(const char *UNUSED(arg))
1515{
1516 done = 1;
1517 return 0;
1518}
1519
1520int
1521cmd_help(const char *arg)
1522{
1523 int i;
1524 char *args = strdup(arg);
1525 char *cmd = NULL;
1526
1527 strtok(args, " ");
1528 if ((cmd = strtok(NULL, " ")) == NULL) {
1529
1530generic_help:
1531 fprintf(stdout, "Available commands:\n");
1532
1533 for (i = 0; commands[i].name; i++) {
1534 if (commands[i].helpstring != NULL) {
1535 fprintf(stdout, " %-15s %s\n", commands[i].name, commands[i].helpstring);
1536 }
1537 }
1538 } else {
1539 /* print specific help for the selected command */
1540
1541 /* get the command of the specified name */
1542 for (i = 0; commands[i].name; i++) {
1543 if (strcmp(cmd, commands[i].name) == 0) {
1544 break;
1545 }
1546 }
1547
1548 /* execute the command's help if any valid command specified */
1549 if (commands[i].name) {
1550 if (commands[i].help_func != NULL) {
1551 commands[i].help_func();
1552 } else {
1553 printf("%s\n", commands[i].helpstring);
1554 }
1555 } else {
1556 /* if unknown command specified, print the list of commands */
1557 printf("Unknown command \'%s\'\n", cmd);
1558 goto generic_help;
1559 }
1560 }
1561
1562 free(args);
1563 return 0;
1564}
1565
1566COMMAND commands[] = {
1567 {"help", cmd_help, NULL, "Display commands description"},
1568 {"add", cmd_add, cmd_add_help, "Add a new model from a specific file"},
1569 {"load", cmd_load, cmd_load_help, "Load a new model from the searchdirs"},
1570 {"print", cmd_print, cmd_print_help, "Print a model"},
Radek Krejcied5acc52019-04-25 15:57:04 +02001571 {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
Radek Krejcie7b95092019-05-15 11:03:07 +02001572#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001573 {"xpath", cmd_xpath, cmd_xpath_help, "Get data nodes satisfying an XPath expression"},
1574 {"list", cmd_list, cmd_list_help, "List all the loaded models"},
1575#endif
1576 {"feature", cmd_feature, cmd_feature_help, "Print/enable/disable all/specific features of models"},
1577 {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for models"},
1578 {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded models"},
1579 {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
1580#ifndef NDEBUG
1581 {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
1582#endif
1583 {"quit", cmd_quit, NULL, "Quit the program"},
1584 /* synonyms for previous commands */
1585 {"?", cmd_help, NULL, "Display commands description"},
1586 {"exit", cmd_quit, NULL, "Quit the program"},
1587 {NULL, NULL, NULL, NULL}
1588};