blob: a8030edd9eb4a3ebb680569c998b0e05dc069718 [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;
177 /* TODO YIN parser not yet implemented
178 } else if (!strcmp(ptr, "yin")) {
179 return LYS_IN_YIN;
180 */
181 } else {
182 fprintf(stderr, "Input file in an unknown format \"%s\".\n", ptr);
183 return LYS_IN_UNKNOWN;
184 }
185 } else {
186 fprintf(stdout, "Input file \"%s\" without extension - unknown format.\n", path);
187 return LYS_IN_UNKNOWN;
188 }
189}
190
191int
192cmd_add(const char *arg)
193{
194 int path_len, ret = 1, index = 0;
195 char *path, *dir, *s, *arg_ptr;
196 const char * const *searchpaths;
197 const struct lys_module *model;
198 LYS_INFORMAT format = LYS_IN_UNKNOWN;
199
200 if (strlen(arg) < 5) {
201 cmd_add_help();
202 return 1;
203 }
204
205 arg_ptr = strdup(arg + 3 /* ignore "add" */);
206
207 for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
208 if (s[2] == '\0' || s[2] == ' ') {
209 ly_ctx_set_option(ctx, LY_CTX_ALLIMPLEMENTED);
210 s[0] = s[1] = ' ';
211 }
212 }
213 s = arg_ptr;
214
215 while (arg_ptr[0] == ' ') {
216 ++arg_ptr;
217 }
218 if (strchr(arg_ptr, ' ')) {
219 path_len = strchr(arg_ptr, ' ') - arg_ptr;
220 } else {
221 path_len = strlen(arg_ptr);
222 }
223 path = strndup(arg_ptr, path_len);
224
225 searchpaths = ly_ctx_get_searchdirs(ctx);
226 if (searchpaths) {
227 for (index = 0; searchpaths[index]; index++);
228 }
229
230 while (path) {
231 format = get_schema_format(path);
232 if (format == LYS_IN_UNKNOWN) {
233 free(path);
234 goto cleanup;
235 }
236
237 dir = strdup(path);
238 ly_ctx_set_searchdir(ctx, dirname(dir));
239 model = lys_parse_path(ctx, path, format);
240 ly_ctx_unset_searchdir(ctx, index);
241 free(path);
242 free(dir);
243
244 if (!model) {
245 /* libyang printed the error messages */
246 goto cleanup;
247 }
248
249 /* next model */
250 arg_ptr += path_len;
251 while (arg_ptr[0] == ' ') {
252 ++arg_ptr;
253 }
254 if (strchr(arg_ptr, ' ')) {
255 path_len = strchr(arg_ptr, ' ') - arg_ptr;
256 } else {
257 path_len = strlen(arg_ptr);
258 }
259
260 if (path_len) {
261 path = strndup(arg_ptr, path_len);
262 } else {
263 path = NULL;
264 }
265 }
266 if (format == LYS_IN_UNKNOWN) {
267 /* no schema on input */
268 cmd_add_help();
269 goto cleanup;
270 }
271 ret = 0;
272
273cleanup:
274 free(s);
275 ly_ctx_unset_option(ctx, LY_CTX_ALLIMPLEMENTED);
276
277 return ret;
278}
279
280int
281cmd_load(const char *arg)
282{
283 int name_len, ret = 1;
284 char *name, *s, *arg_ptr;
285 const struct lys_module *model;
286
287 if (strlen(arg) < 6) {
288 cmd_load_help();
289 return 1;
290 }
291
292 arg_ptr = strdup(arg + 4 /* ignore "load" */);
293
294 for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
295 if (s[2] == '\0' || s[2] == ' ') {
296 ly_ctx_set_option(ctx, LY_CTX_ALLIMPLEMENTED);
297 s[0] = s[1] = ' ';
298 }
299 }
300 s = arg_ptr;
301
302 while (arg_ptr[0] == ' ') {
303 ++arg_ptr;
304 }
305 if (strchr(arg_ptr, ' ')) {
306 name_len = strchr(arg_ptr, ' ') - arg_ptr;
307 } else {
308 name_len = strlen(arg_ptr);
309 }
310 name = strndup(arg_ptr, name_len);
311
312 while (name) {
313 model = ly_ctx_load_module(ctx, name, NULL);
314 free(name);
315 if (!model) {
316 /* libyang printed the error messages */
317 goto cleanup;
318 }
319
320 /* next model */
321 arg_ptr += name_len;
322 while (arg_ptr[0] == ' ') {
323 ++arg_ptr;
324 }
325 if (strchr(arg_ptr, ' ')) {
326 name_len = strchr(arg_ptr, ' ') - arg_ptr;
327 } else {
328 name_len = strlen(arg_ptr);
329 }
330
331 if (name_len) {
332 name = strndup(arg_ptr, name_len);
333 } else {
334 name = NULL;
335 }
336 }
337 ret = 0;
338
339cleanup:
340 free(s);
341 ly_ctx_unset_option(ctx, LY_CTX_ALLIMPLEMENTED);
342
343 return ret;
344}
345
346int
347cmd_print(const char *arg)
348{
Radek Krejci693262f2019-04-29 15:23:20 +0200349 int c, argc, option_index, ret = 1, tree_ll = 0, tree_opts = 0, compiled = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200350 char **argv = NULL, *ptr, *model_name, *revision;
351 const char *out_path = NULL;
352 const struct lys_module *module;
353 LYS_OUTFORMAT format = LYS_OUT_TREE;
354 FILE *output = stdout;
355 static struct option long_options[] = {
Radek Krejci693262f2019-04-29 15:23:20 +0200356 {"compiled", no_argument, 0, 'c'},
Radek Krejcied5acc52019-04-25 15:57:04 +0200357 {"help", no_argument, 0, 'h'},
358 {"format", required_argument, 0, 'f'},
359 {"output", required_argument, 0, 'o'},
360#if 0
361 {"tree-print-groupings", no_argument, 0, 'g'},
362 {"tree-print-uses", no_argument, 0, 'u'},
363 {"tree-no-leafref-target", no_argument, 0, 'n'},
364 {"tree-path", required_argument, 0, 'P'},
365 {"info-path", required_argument, 0, 'P'},
366 {"tree-line-length", required_argument, 0, 'L'},
367#endif
368 {NULL, 0, 0, 0}
369 };
370 void *rlcd;
371
372 argc = 1;
373 argv = malloc(2*sizeof *argv);
374 *argv = strdup(arg);
375 ptr = strtok(*argv, " ");
376 while ((ptr = strtok(NULL, " "))) {
377 rlcd = realloc(argv, (argc+2)*sizeof *argv);
378 if (!rlcd) {
379 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
380 goto cleanup;
381 }
382 argv = rlcd;
383 argv[argc++] = ptr;
384 }
385 argv[argc] = NULL;
386
387 optind = 0;
388 while (1) {
389 option_index = 0;
Radek Krejci693262f2019-04-29 15:23:20 +0200390 c = getopt_long(argc, argv, "chf:go:guP:L:", long_options, &option_index);
Radek Krejcied5acc52019-04-25 15:57:04 +0200391 if (c == -1) {
392 break;
393 }
394
395 switch (c) {
Radek Krejci693262f2019-04-29 15:23:20 +0200396 case 'c':
397 compiled = 1;
398 break;
Radek Krejcied5acc52019-04-25 15:57:04 +0200399 case 'h':
400 cmd_print_help();
401 ret = 0;
402 goto cleanup;
403 case 'f':
404 if (!strcmp(optarg, "yang")) {
405 format = LYS_OUT_YANG;
406#if 0
407 } else if (!strcmp(optarg, "yin")) {
408 format = LYS_OUT_YIN;
409 } else if (!strcmp(optarg, "tree")) {
410 format = LYS_OUT_TREE;
411 } else if (!strcmp(optarg, "tree-rfc")) {
412 format = LYS_OUT_TREE;
413 tree_opts |= LYS_OUTOPT_TREE_RFC;
414 } else if (!strcmp(optarg, "info")) {
415 format = LYS_OUT_INFO;
416 } else if (!strcmp(optarg, "jsons")) {
417 format = LYS_OUT_JSON;
418#endif
419 } else {
420 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
421 goto cleanup;
422 }
423 break;
424 case 'o':
425 if (out_path) {
426 fprintf(stderr, "Output specified twice.\n");
427 goto cleanup;
428 }
429 out_path = optarg;
430 break;
431#if 0
432 case 'g':
433 tree_opts |= LYS_OUTOPT_TREE_GROUPING;
434 break;
435 case 'u':
436 tree_opts |= LYS_OUTOPT_TREE_USES;
437 break;
438 case 'n':
439 tree_opts |= LYS_OUTOPT_TREE_NO_LEAFREF;
440 break;
441 case 'P':
442 target_path = optarg;
443 break;
444 case 'L':
445 tree_ll = atoi(optarg);
446 break;
447#endif
448 case '?':
449 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
450 goto cleanup;
451 }
452 }
453
454 /* file name */
455 if (optind == argc) {
456 fprintf(stderr, "Missing the module name.\n");
457 goto cleanup;
458 }
459
Radek Krejci693262f2019-04-29 15:23:20 +0200460 /* compiled format */
461 if (compiled) {
462 format++;
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 */
466 if ((tree_opts || tree_ll) && format != LYS_OUT_TREE) {
467 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 /* module, revision */
471 model_name = argv[optind];
472 revision = NULL;
473 if (strchr(model_name, '@')) {
474 revision = strchr(model_name, '@');
475 revision[0] = '\0';
476 ++revision;
477 }
478
479 if (revision) {
480 module = ly_ctx_get_module(ctx, model_name, revision);
481 } else {
482 module = ly_ctx_get_module_latest(ctx, model_name);
483 }
484#if 0
485 if (!module) {
486 /* not a module, try to find it as a submodule */
487 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
488 }
489#endif
490
491 if (!module) {
492 if (revision) {
493 fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
494 } else {
495 fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
496 }
497 goto cleanup;
498 }
499
500 if (out_path) {
501 output = fopen(out_path, "w");
502 if (!output) {
503 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
504 goto cleanup;
505 }
506 }
507
508 ret = lys_print_file(output, module, format, tree_ll, tree_opts);
509 if (format == LYS_OUT_JSON) {
510 fputs("\n", output);
511 }
512
513cleanup:
514 free(*argv);
515 free(argv);
516
517 if (output && (output != stdout)) {
518 fclose(output);
519 }
520
521 return ret;
522}
Radek Krejcie7b95092019-05-15 11:03:07 +0200523
Radek Krejcied5acc52019-04-25 15:57:04 +0200524static LYD_FORMAT
525detect_data_format(char *filepath)
526{
527 size_t len;
528
529 /* detect input format according to file suffix */
530 len = strlen(filepath);
531 for (; isspace(filepath[len - 1]); len--, filepath[len] = '\0'); /* remove trailing whitespaces */
532 if (len >= 5 && !strcmp(&filepath[len - 4], ".xml")) {
533 return LYD_XML;
Radek Krejcie7b95092019-05-15 11:03:07 +0200534#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200535 } else if (len >= 6 && !strcmp(&filepath[len - 5], ".json")) {
536 return LYD_JSON;
537 } else if (len >= 5 && !strcmp(&filepath[len - 4], ".lyb")) {
538 return LYD_LYB;
Radek Krejcie7b95092019-05-15 11:03:07 +0200539#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200540 } else {
541 return LYD_UNKNOWN;
542 }
543}
544
545static int
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200546parse_data(char *filepath, int *options, const struct lyd_node **trees, const char *rpc_act_file,
Radek Krejcied5acc52019-04-25 15:57:04 +0200547 struct lyd_node **result)
548{
549 LYD_FORMAT informat = LYD_UNKNOWN;
Radek Krejcied5acc52019-04-25 15:57:04 +0200550 struct lyd_node *data = NULL, *rpc_act = NULL;
551 int opts = *options;
552
553 /* detect input format according to file suffix */
554 informat = detect_data_format(filepath);
555 if (informat == LYD_UNKNOWN) {
556 fprintf(stderr, "Unable to resolve format of the input file, please add \".xml\", \".json\", or \".lyb\" suffix.\n");
557 return EXIT_FAILURE;
558 }
559
Radek Krejcie7b95092019-05-15 11:03:07 +0200560 ly_err_clean(ctx, NULL);
Radek Krejcied5acc52019-04-25 15:57:04 +0200561
Radek Krejcie7b95092019-05-15 11:03:07 +0200562#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200563 if ((opts & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
564 /* automatically detect data type from the data top level */
565 if (informat != LYD_XML) {
566 fprintf(stderr, "Only XML data can be automatically explored.\n");
567 return EXIT_FAILURE;
568 }
569
570 xml = lyxml_parse_path(ctx, filepath, 0);
571 if (!xml) {
572 fprintf(stderr, "Failed to parse XML data for automatic type detection.\n");
573 return EXIT_FAILURE;
574 }
575
576 /* NOTE: namespace is ignored to simplify usage of this feature */
577
578 if (!strcmp(xml->name, "data")) {
579 fprintf(stdout, "Parsing %s as complete datastore.\n", filepath);
580 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_ADD_YANGLIB;
581 } else if (!strcmp(xml->name, "config")) {
582 fprintf(stdout, "Parsing %s as config data.\n", filepath);
583 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
584 } else if (!strcmp(xml->name, "get-reply")) {
585 fprintf(stdout, "Parsing %s as <get> reply data.\n", filepath);
586 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
587 } else if (!strcmp(xml->name, "get-config-reply")) {
588 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", filepath);
589 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
590 } else if (!strcmp(xml->name, "edit-config")) {
591 fprintf(stdout, "Parsing %s as <edit-config> data.\n", filepath);
592 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
593 } else if (!strcmp(xml->name, "rpc")) {
594 fprintf(stdout, "Parsing %s as <rpc> data.\n", filepath);
595 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
596 } else if (!strcmp(xml->name, "rpc-reply")) {
597 if (!rpc_act_file) {
598 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
599 lyxml_free(ctx, xml);
600 return EXIT_FAILURE;
601 }
602 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", filepath);
603 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
604 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, val_tree);
605 if (!rpc_act) {
606 fprintf(stderr, "Failed to parse RPC/action.\n");
607 lyxml_free(ctx, xml);
608 return EXIT_FAILURE;
609 }
610 } else if (!strcmp(xml->name, "notification")) {
611 fprintf(stdout, "Parsing %s as <notification> data.\n", filepath);
612 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
613 } else if (!strcmp(xml->name, "yang-data")) {
614 fprintf(stdout, "Parsing %s as <yang-data> data.\n", filepath);
615 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
616 if (!rpc_act_file) {
617 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
618 lyxml_free(ctx, xml);
619 return EXIT_FAILURE;
620 }
621 } else {
622 fprintf(stderr, "Invalid top-level element for automatic data type recognition.\n");
623 lyxml_free(ctx, xml);
624 return EXIT_FAILURE;
625 }
626
627 if (opts & LYD_OPT_RPCREPLY) {
628 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act, val_tree);
629 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
630 data = lyd_parse_xml(ctx, &xml->child, opts, val_tree);
631 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
632 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act_file);
633 } else {
634 data = lyd_parse_xml(ctx, &xml->child, opts);
635 }
636 lyxml_free(ctx, xml);
637 } else {
Radek Krejcie7b95092019-05-15 11:03:07 +0200638#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200639 if (opts & LYD_OPT_RPCREPLY) {
640 if (!rpc_act_file) {
641 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
642 return EXIT_FAILURE;
643 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200644 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200645 if (!rpc_act) {
646 fprintf(stderr, "Failed to parse RPC/action.\n");
647 return EXIT_FAILURE;
648 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200649 if (trees) {
650 const struct lyd_node **trees_new;
651 unsigned int u;
652 trees_new = lyd_trees_new(1, rpc_act);
653
654 LY_ARRAY_FOR(trees, u) {
655 trees_new = lyd_trees_add(trees_new, trees[u]);
656 }
657 lyd_trees_free(trees, 0);
658 trees = trees_new;
659 } else {
660 trees = lyd_trees_new(1, rpc_act);
661 }
662 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200663 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200664 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
665#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200666 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
667 if (!rpc_act_file) {
668 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
669 return EXIT_FAILURE;
670 }
671 data = lyd_parse_path(ctx, filepath, informat, opts, rpc_act_file);
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200672#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200673 } else {
674 if (!(opts & LYD_OPT_TYPEMASK)) {
675 /* automatically add yang-library data */
676 opts |= LYD_OPT_DATA_ADD_YANGLIB;
677 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200678 data = lyd_parse_path(ctx, filepath, informat, opts, NULL);
Radek Krejcied5acc52019-04-25 15:57:04 +0200679 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200680#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200681 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200682#endif
683 lyd_free_all(rpc_act);
Radek Krejcied5acc52019-04-25 15:57:04 +0200684
Radek Krejcie7b95092019-05-15 11:03:07 +0200685 if (ly_err_first(ctx)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200686 fprintf(stderr, "Failed to parse data.\n");
Radek Krejcie7b95092019-05-15 11:03:07 +0200687 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200688 return EXIT_FAILURE;
689 }
690
691 *result = data;
692 *options = opts;
693 return EXIT_SUCCESS;
694}
695
696int
697cmd_data(const char *arg)
698{
699 int c, argc, option_index, ret = 1;
700 int options = 0, printopt = 0;
701 char **argv = NULL, *ptr;
702 const char *out_path = NULL;
703 struct lyd_node *data = NULL, *val_tree = NULL;
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200704 const struct lyd_node **trees = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200705 LYD_FORMAT outformat = LYD_UNKNOWN;
706 FILE *output = stdout;
707 static struct option long_options[] = {
708 {"defaults", required_argument, 0, 'd'},
709 {"help", no_argument, 0, 'h'},
710 {"format", required_argument, 0, 'f'},
711 {"option", required_argument, 0, 't'},
712 {"output", required_argument, 0, 'o'},
713 {"running", required_argument, 0, 'r'},
714 {"strict", no_argument, 0, 's'},
715 {NULL, 0, 0, 0}
716 };
717 void *rlcd;
718
719 argc = 1;
720 argv = malloc(2*sizeof *argv);
721 *argv = strdup(arg);
722 ptr = strtok(*argv, " ");
723 while ((ptr = strtok(NULL, " "))) {
724 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
725 if (!rlcd) {
726 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
727 goto cleanup;
728 }
729 argv = rlcd;
730 argv[argc++] = ptr;
731 }
732 argv[argc] = NULL;
733
734 optind = 0;
735 while (1) {
736 option_index = 0;
737 c = getopt_long(argc, argv, "d:hf:o:st:r:", long_options, &option_index);
738 if (c == -1) {
739 break;
740 }
741
742 switch (c) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200743#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200744 case 'd':
745 if (!strcmp(optarg, "all")) {
746 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL;
747 } else if (!strcmp(optarg, "all-tagged")) {
748 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
749 } else if (!strcmp(optarg, "trim")) {
750 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_TRIM;
751 } else if (!strcmp(optarg, "implicit-tagged")) {
752 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
753 }
754 break;
Radek Krejcie7b95092019-05-15 11:03:07 +0200755#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200756 case 'h':
757 cmd_data_help();
758 ret = 0;
759 goto cleanup;
760 case 'f':
761 if (!strcmp(optarg, "xml")) {
762 outformat = LYD_XML;
Radek Krejcie7b95092019-05-15 11:03:07 +0200763#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200764 } else if (!strcmp(optarg, "json")) {
765 outformat = LYD_JSON;
766 } else if (!strcmp(optarg, "lyb")) {
767 outformat = LYD_LYB;
Radek Krejcie7b95092019-05-15 11:03:07 +0200768#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200769 } else {
770 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
771 goto cleanup;
772 }
773 break;
774 case 'o':
775 if (out_path) {
776 fprintf(stderr, "Output specified twice.\n");
777 goto cleanup;
778 }
779 out_path = optarg;
780 break;
781 case 'r':
Radek Krejcied5acc52019-04-25 15:57:04 +0200782 if (optarg[0] == '!') {
783 /* ignore extenral dependencies to the running datastore */
784 options |= LYD_OPT_NOEXTDEPS;
785 } else {
786 /* external file with the running datastore */
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200787 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_DATA_NO_YANGLIB, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200788 if (!val_tree) {
789 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
790 goto cleanup;
791 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200792 if (!trees) {
793 trees = lyd_trees_new(1, val_tree);
794 } else {
795 trees = lyd_trees_add(trees, val_tree);
796 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200797 }
798 break;
799 case 's':
800 options |= LYD_OPT_STRICT;
801 options |= LYD_OPT_OBSOLETE;
802 break;
803 case 't':
804 if (!strcmp(optarg, "auto")) {
805 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
806 } else if (!strcmp(optarg, "data")) {
807 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA;
808 } else if (!strcmp(optarg, "config")) {
809 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
810 } else if (!strcmp(optarg, "get")) {
811 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
812 } else if (!strcmp(optarg, "getconfig")) {
813 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
814 } else if (!strcmp(optarg, "edit")) {
815 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
816 } else if (!strcmp(optarg, "rpc")) {
817 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
818 } else if (!strcmp(optarg, "rpcreply")) {
819 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
820 } else if (!strcmp(optarg, "notif")) {
821 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
822 } else if (!strcmp(optarg, "yangdata")) {
823 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
824 } else {
825 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
826 cmd_data_help();
827 goto cleanup;
828 }
829 break;
830 case '?':
831 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
832 goto cleanup;
833 }
834 }
835
836 /* file name */
837 if (optind == argc) {
838 fprintf(stderr, "Missing the data file name.\n");
839 goto cleanup;
840 }
841
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200842 if (parse_data(argv[optind], &options, trees, argv[optind + 1], &data)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200843 goto cleanup;
844 }
845
846 if (out_path) {
847 output = fopen(out_path, "w");
848 if (!output) {
849 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
850 goto cleanup;
851 }
852 }
853
854 if (outformat != LYD_UNKNOWN) {
855 if (options & LYD_OPT_RPCREPLY) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200856 lyd_print_file(output, lyd_node_children(data), outformat, LYDP_WITHSIBLINGS | LYDP_FORMAT | printopt);
Radek Krejcied5acc52019-04-25 15:57:04 +0200857 } else {
Radek Krejcie7b95092019-05-15 11:03:07 +0200858 lyd_print_file(output, data, outformat, LYDP_WITHSIBLINGS | LYDP_FORMAT | printopt);
Radek Krejcied5acc52019-04-25 15:57:04 +0200859 }
860 }
861
862 ret = 0;
863
864cleanup:
865 free(*argv);
866 free(argv);
867
868 if (output && (output != stdout)) {
869 fclose(output);
870 }
871
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200872 lyd_trees_free(trees, 1);
Radek Krejcie7b95092019-05-15 11:03:07 +0200873 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200874
875 return ret;
876}
Radek Krejcie7b95092019-05-15 11:03:07 +0200877#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200878int
879cmd_xpath(const char *arg)
880{
881 int c, argc, option_index, ret = 1, long_str;
882 char **argv = NULL, *ptr, *expr = NULL;
883 unsigned int i, j;
884 int options = 0;
885 struct lyd_node *data = NULL, *node, *val_tree = NULL;
886 struct lyd_node_leaf_list *key;
887 struct ly_set *set;
888 static struct option long_options[] = {
889 {"help", no_argument, 0, 'h'},
890 {"expr", required_argument, 0, 'e'},
891 {NULL, 0, 0, 0}
892 };
893 void *rlcd;
894
895 long_str = 0;
896 argc = 1;
897 argv = malloc(2 * sizeof *argv);
898 *argv = strdup(arg);
899 ptr = strtok(*argv, " ");
900 while ((ptr = strtok(NULL, " "))) {
901 if (long_str) {
902 ptr[-1] = ' ';
903 if (ptr[strlen(ptr) - 1] == long_str) {
904 long_str = 0;
905 ptr[strlen(ptr) - 1] = '\0';
906 }
907 } else {
908 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
909 if (!rlcd) {
910 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
911 goto cleanup;
912 }
913 argv = rlcd;
914 argv[argc] = ptr;
915 if (ptr[0] == '"') {
916 long_str = '"';
917 ++argv[argc];
918 }
919 if (ptr[0] == '\'') {
920 long_str = '\'';
921 ++argv[argc];
922 }
923 if (ptr[strlen(ptr) - 1] == long_str) {
924 long_str = 0;
925 ptr[strlen(ptr) - 1] = '\0';
926 }
927 ++argc;
928 }
929 }
930 argv[argc] = NULL;
931
932 optind = 0;
933 while (1) {
934 option_index = 0;
935 c = getopt_long(argc, argv, "he:t:x:", long_options, &option_index);
936 if (c == -1) {
937 break;
938 }
939
940 switch (c) {
941 case 'h':
942 cmd_xpath_help();
943 ret = 0;
944 goto cleanup;
945 case 'e':
946 expr = optarg;
947 break;
948 case 't':
949 if (!strcmp(optarg, "auto")) {
950 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
951 } else if (!strcmp(optarg, "config")) {
952 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
953 } else if (!strcmp(optarg, "get")) {
954 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
955 } else if (!strcmp(optarg, "getconfig")) {
956 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
957 } else if (!strcmp(optarg, "edit")) {
958 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
959 } else if (!strcmp(optarg, "rpc")) {
960 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
961 } else if (!strcmp(optarg, "rpcreply")) {
962 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
963 } else if (!strcmp(optarg, "notif")) {
964 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
965 } else if (!strcmp(optarg, "yangdata")) {
966 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
967 } else {
968 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
969 cmd_data_help();
970 goto cleanup;
971 }
972 break;
973 case 'x':
974 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_CONFIG);
975 if (!val_tree) {
976 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
977 goto cleanup;
978 }
979 break;
980 case '?':
981 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
982 goto cleanup;
983 }
984 }
985
986 if (optind == argc) {
987 fprintf(stderr, "Missing the file with data.\n");
988 goto cleanup;
989 }
990
991 if (!expr) {
992 fprintf(stderr, "Missing the XPath expression.\n");
993 goto cleanup;
994 }
995
996 if (parse_data(argv[optind], &options, val_tree, argv[optind + 1], &data)) {
997 goto cleanup;
998 }
999
1000 if (!(set = lyd_find_path(data, expr))) {
1001 goto cleanup;
1002 }
1003
1004 /* print result */
1005 printf("Result:\n");
1006 if (!set->number) {
1007 printf("\tEmpty\n");
1008 } else {
1009 for (i = 0; i < set->number; ++i) {
1010 node = set->set.d[i];
1011 switch (node->schema->nodetype) {
1012 case LYS_CONTAINER:
1013 printf("\tContainer ");
1014 break;
1015 case LYS_LEAF:
1016 printf("\tLeaf ");
1017 break;
1018 case LYS_LEAFLIST:
1019 printf("\tLeaflist ");
1020 break;
1021 case LYS_LIST:
1022 printf("\tList ");
1023 break;
1024 case LYS_ANYXML:
1025 printf("\tAnyxml ");
1026 break;
1027 case LYS_ANYDATA:
1028 printf("\tAnydata ");
1029 break;
1030 default:
1031 printf("\tUnknown ");
1032 break;
1033 }
1034 printf("\"%s\"", node->schema->name);
1035 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
1036 printf(" (val: %s)", ((struct lyd_node_leaf_list *)node)->value_str);
1037 } else if (node->schema->nodetype == LYS_LIST) {
1038 key = (struct lyd_node_leaf_list *)node->child;
1039 printf(" (");
1040 for (j = 0; j < ((struct lys_node_list *)node->schema)->keys_size; ++j) {
1041 if (j) {
1042 printf(" ");
1043 }
1044 printf("\"%s\": %s", key->schema->name, key->value_str);
1045 key = (struct lyd_node_leaf_list *)key->next;
1046 }
1047 printf(")");
1048 }
1049 printf("\n");
1050 }
1051 }
1052 printf("\n");
1053
1054 ly_set_free(set);
1055 ret = 0;
1056
1057cleanup:
1058 free(*argv);
1059 free(argv);
1060
1061 lyd_free_withsiblings(data);
1062
1063 return ret;
1064}
1065
1066int
1067print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
1068{
1069 struct lyd_node *ylib;
1070 uint32_t idx = 0, has_modules = 0;
1071 uint8_t u;
1072 const struct lys_module *mod;
1073
1074 if (outformat != LYD_UNKNOWN) {
1075 ylib = ly_ctx_info(ctx);
1076 if (!ylib) {
1077 fprintf(stderr, "Getting context info (ietf-yang-library data) failed.\n");
1078 return 1;
1079 }
1080
1081 lyd_print_file(out, ylib, outformat, LYP_WITHSIBLINGS | LYP_FORMAT);
1082 lyd_free_withsiblings(ylib);
1083 return 0;
1084 }
1085
1086 /* iterate schemas in context and provide just the basic info */
1087 fprintf(out, "List of the loaded models:\n");
1088 while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
1089 has_modules++;
1090
1091 /* conformance print */
1092 if (mod->implemented) {
1093 fprintf(out, "\tI");
1094 } else {
1095 fprintf(out, "\ti");
1096 }
1097
1098 /* module print */
1099 fprintf(out, " %s", mod->name);
1100 if (mod->rev_size) {
1101 fprintf(out, "@%s", mod->rev[0].date);
1102 }
1103
1104 /* submodules print */
1105 if (mod->inc_size) {
1106 fprintf(out, " (");
1107 for (u = 0; u < mod->inc_size; u++) {
1108 fprintf(out, "%s%s", !u ? "" : ",", mod->inc[u].submodule->name);
1109 if (mod->inc[u].submodule->rev_size) {
1110 fprintf(out, "@%s", mod->inc[u].submodule->rev[0].date);
1111 }
1112 }
1113 fprintf(out, ")");
1114 }
1115
1116 /* finish the line */
1117 fprintf(out, "\n");
1118 }
1119
1120 if (!has_modules) {
1121 fprintf(out, "\t(none)\n");
1122 }
1123
1124 return 0;
1125}
1126
1127int
1128cmd_list(const char *arg)
1129{
1130 char **argv = NULL, *ptr;
1131 int c, argc, option_index;
1132 LYD_FORMAT outformat = LYD_UNKNOWN;
1133 static struct option long_options[] = {
1134 {"help", no_argument, 0, 'h'},
1135 {"format", required_argument, 0, 'f'},
1136 {NULL, 0, 0, 0}
1137 };
1138 void *rlcd;
1139
1140 argc = 1;
1141 argv = malloc(2*sizeof *argv);
1142 *argv = strdup(arg);
1143 ptr = strtok(*argv, " ");
1144 while ((ptr = strtok(NULL, " "))) {
1145 rlcd = realloc(argv, (argc+2)*sizeof *argv);
1146 if (!rlcd) {
1147 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1148 goto error;
1149 }
1150 argv = rlcd;
1151 argv[argc++] = ptr;
1152 }
1153 argv[argc] = NULL;
1154
1155 optind = 0;
1156 while (1) {
1157 option_index = 0;
1158 c = getopt_long(argc, argv, "hf:", long_options, &option_index);
1159 if (c == -1) {
1160 break;
1161 }
1162
1163 switch (c) {
1164 case 'h':
1165 cmd_data_help();
1166 free(*argv);
1167 free(argv);
1168 return 0;
1169 case 'f':
1170 if (!strcmp(optarg, "xml")) {
1171 outformat = LYD_XML;
1172 } else if (!strcmp(optarg, "json")) {
1173 outformat = LYD_JSON;
1174 } else {
1175 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
1176 goto error;
1177 }
1178 break;
1179 case '?':
1180 /* getopt_long() prints message */
1181 goto error;
1182 }
1183 }
1184 if (optind != argc) {
1185 fprintf(stderr, "Unknown parameter \"%s\"\n", argv[optind]);
1186error:
1187 free(*argv);
1188 free(argv);
1189 return 1;
1190 }
1191 free(*argv);
1192 free(argv);
1193
1194 return print_list(stdout, ctx, outformat);
1195}
1196#endif
1197int
1198cmd_feature(const char *arg)
1199{
1200 int c, argc, option_index, ret = 1, task = 0;
1201 char **argv = NULL, *ptr, *model_name, *revision, *feat_names = NULL;
1202 const struct lys_module *module;
1203 static struct option long_options[] = {
1204 {"help", no_argument, 0, 'h'},
1205 {"enable", required_argument, 0, 'e'},
1206 {"disable", required_argument, 0, 'd'},
1207 {NULL, 0, 0, 0}
1208 };
1209 void *rlcd;
1210
1211 argc = 1;
1212 argv = malloc(2*sizeof *argv);
1213 *argv = strdup(arg);
1214 ptr = strtok(*argv, " ");
1215 while ((ptr = strtok(NULL, " "))) {
1216 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
1217 if (!rlcd) {
1218 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1219 goto cleanup;
1220 }
1221 argv = rlcd;
1222 argv[argc++] = ptr;
1223 }
1224 argv[argc] = NULL;
1225
1226 optind = 0;
1227 while (1) {
1228 option_index = 0;
1229 c = getopt_long(argc, argv, "he:d:", long_options, &option_index);
1230 if (c == -1) {
1231 break;
1232 }
1233
1234 switch (c) {
1235 case 'h':
1236 cmd_feature_help();
1237 ret = 0;
1238 goto cleanup;
1239 case 'e':
1240 if (task) {
1241 fprintf(stderr, "Only one of enable or disable can be specified.\n");
1242 goto cleanup;
1243 }
1244 task = 1;
1245 feat_names = optarg;
1246 break;
1247 case 'd':
1248 if (task) {
1249 fprintf(stderr, "Only one of enable, or disable can be specified.\n");
1250 goto cleanup;
1251 }
1252 task = 2;
1253 feat_names = optarg;
1254 break;
1255 case '?':
1256 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
1257 goto cleanup;
1258 }
1259 }
1260
1261 /* module name */
1262 if (optind == argc) {
1263 fprintf(stderr, "Missing the module name.\n");
1264 goto cleanup;
1265 }
1266
1267 revision = NULL;
1268 model_name = argv[optind];
1269 if (strchr(model_name, '@')) {
1270 revision = strchr(model_name, '@');
1271 revision[0] = '\0';
1272 ++revision;
1273 }
1274
1275 module = ly_ctx_get_module(ctx, model_name, revision);
1276#if 0
1277 if (!module) {
1278 /* not a module, try to find it as a submodule */
1279 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
1280 }
1281#endif
1282
1283 if (module == NULL) {
1284 if (revision) {
1285 fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
1286 } else {
1287 fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
1288 }
1289 goto cleanup;
1290 }
1291
1292 if (!task) {
1293 unsigned int len, max_len = 0;
1294 unsigned int u;
1295 struct lysc_feature *features;
1296
1297 printf("%s features:\n", module->name);
1298
1299 if (module->compiled) {
1300 features = module->compiled->features;
1301 } else {
1302 features = module->off_features;
1303 }
1304
1305 /* get the max len */
1306 LY_ARRAY_FOR(features, u) {
1307 len = strlen(features[u].name);
1308 if (len > max_len) {
1309 max_len = len;
1310 }
1311 }
1312
1313 LY_ARRAY_FOR(features, u) {
1314 printf("\t%-*s (%s)\n", max_len, features[u].name, (features[u].flags & LYS_FENABLED) ? "on" : "off");
1315 }
1316 if (!u) {
1317 printf("\t(none)\n");
1318 }
1319 } else {
1320 feat_names = strtok(feat_names, ",");
1321 while (feat_names) {
1322 if (((task == 1) && lys_feature_enable(module, feat_names))
1323 || ((task == 2) && lys_feature_disable(module, feat_names))) {
1324 fprintf(stderr, "Feature \"%s\" not found.\n", feat_names);
1325 ret = 1;
1326 }
1327 feat_names = strtok(NULL, ",");
1328 }
1329 }
1330
1331cleanup:
1332 free(*argv);
1333 free(argv);
1334
1335 return ret;
1336}
1337
1338int
1339cmd_searchpath(const char *arg)
1340{
1341 const char *path;
1342 const char * const *searchpaths;
1343 int index;
1344 struct stat st;
1345
1346 for (path = strchr(arg, ' '); path && (path[0] == ' '); ++path);
1347 if (!path || (path[0] == '\0')) {
1348 searchpaths = ly_ctx_get_searchdirs(ctx);
1349 if (searchpaths) {
1350 for (index = 0; searchpaths[index]; index++) {
1351 fprintf(stdout, "%s\n", searchpaths[index]);
1352 }
1353 }
1354 return 0;
1355 }
1356
1357 if ((!strncmp(path, "-h", 2) && (path[2] == '\0' || path[2] == ' ')) ||
1358 (!strncmp(path, "--help", 6) && (path[6] == '\0' || path[6] == ' '))) {
1359 cmd_searchpath_help();
1360 return 0;
1361 } else if (!strncmp(path, "--clear", 7) && (path[7] == '\0' || path[7] == ' ')) {
1362 ly_ctx_unset_searchdirs(ctx, NULL);
1363 return 0;
1364 }
1365
1366 if (stat(path, &st) == -1) {
1367 fprintf(stderr, "Failed to stat the search path (%s).\n", strerror(errno));
1368 return 1;
1369 }
1370 if (!S_ISDIR(st.st_mode)) {
1371 fprintf(stderr, "\"%s\" is not a directory.\n", path);
1372 return 1;
1373 }
1374
1375 ly_ctx_set_searchdir(ctx, path);
1376
1377 return 0;
1378}
1379
1380int
1381cmd_clear(const char *arg)
1382{
1383 struct ly_ctx *ctx_new;
1384 int options = 0;
1385#if 0
1386 int i;
1387 char *ylpath;
1388 const char * const *searchpaths;
1389 LYD_FORMAT format;
1390
1391 /* get optional yang library file name */
1392 for (i = 5; arg[i] && isspace(arg[i]); i++);
1393 if (arg[i]) {
1394 if (arg[i] == '-' && arg[i + 1] == 'e') {
1395 options = LY_CTX_NOYANGLIBRARY;
1396 goto create_empty;
1397 } else {
1398 ylpath = strdup(&arg[i]);
1399 format = detect_data_format(ylpath);
1400 if (format == LYD_UNKNOWN) {
1401 free(ylpath);
1402 fprintf(stderr, "Unable to resolve format of the yang library file, please add \".xml\" or \".json\" suffix.\n");
1403 goto create_empty;
1404 }
1405 searchpaths = ly_ctx_get_searchdirs(ctx);
1406 ctx_new = ly_ctx_new_ylpath(searchpaths ? searchpaths[0] : NULL, ylpath, format, 0);
1407 free(ylpath);
1408 }
1409 } else {
1410create_empty:
1411#else
1412 (void) arg; /* TODO unused */
1413 {
1414#endif
1415 ly_ctx_new(NULL, options, &ctx_new);
1416 }
1417
1418 if (!ctx_new) {
1419 fprintf(stderr, "Failed to create context.\n");
1420 return 1;
1421 }
1422
1423 /* final switch */
1424 ly_ctx_destroy(ctx, NULL);
1425 ctx = ctx_new;
1426
1427 return 0;
1428}
1429
1430int
1431cmd_verb(const char *arg)
1432{
1433 const char *verb;
1434 if (strlen(arg) < 5) {
1435 cmd_verb_help();
1436 return 1;
1437 }
1438
1439 verb = arg + 5;
1440 if (!strcmp(verb, "error") || !strcmp(verb, "0")) {
1441 ly_verb(LY_LLERR);
1442#ifndef NDEBUG
1443 ly_verb_dbg(0);
1444#endif
1445 } else if (!strcmp(verb, "warning") || !strcmp(verb, "1")) {
1446 ly_verb(LY_LLWRN);
1447#ifndef NDEBUG
1448 ly_verb_dbg(0);
1449#endif
1450 } else if (!strcmp(verb, "verbose") || !strcmp(verb, "2")) {
1451 ly_verb(LY_LLVRB);
1452#ifndef NDEBUG
1453 ly_verb_dbg(0);
1454#endif
1455 } else if (!strcmp(verb, "debug") || !strcmp(verb, "3")) {
1456 ly_verb(LY_LLDBG);
1457#ifndef NDEBUG
1458 ly_verb_dbg(LY_LDGDICT | LY_LDGYANG | LY_LDGYIN | LY_LDGXPATH | LY_LDGDIFF);
1459#endif
1460 } else {
1461 fprintf(stderr, "Unknown verbosity \"%s\"\n", verb);
1462 return 1;
1463 }
1464
1465 return 0;
1466}
1467
1468#ifndef NDEBUG
1469
1470int
1471cmd_debug(const char *arg)
1472{
1473 const char *beg, *end;
1474 int grps = 0;
1475 if (strlen(arg) < 6) {
1476 cmd_debug_help();
1477 return 1;
1478 }
1479
1480 end = arg + 6;
1481 while (end[0]) {
1482 for (beg = end; isspace(beg[0]); ++beg);
1483 if (!beg[0]) {
1484 break;
1485 }
1486
1487 for (end = beg; (end[0] && !isspace(end[0])); ++end);
1488
1489 if (!strncmp(beg, "dict", end - beg)) {
1490 grps |= LY_LDGDICT;
1491 } else if (!strncmp(beg, "yang", end - beg)) {
1492 grps |= LY_LDGYANG;
1493 } else if (!strncmp(beg, "yin", end - beg)) {
1494 grps |= LY_LDGYIN;
1495 } else if (!strncmp(beg, "xpath", end - beg)) {
1496 grps |= LY_LDGXPATH;
1497 } else if (!strncmp(beg, "diff", end - beg)) {
1498 grps |= LY_LDGDIFF;
1499 } else {
1500 fprintf(stderr, "Unknown debug group \"%.*s\"\n", (int)(end - beg), beg);
1501 return 1;
1502 }
1503 }
1504 ly_verb_dbg(grps);
1505
1506 return 0;
1507}
1508
1509#endif
1510
1511int
1512cmd_quit(const char *UNUSED(arg))
1513{
1514 done = 1;
1515 return 0;
1516}
1517
1518int
1519cmd_help(const char *arg)
1520{
1521 int i;
1522 char *args = strdup(arg);
1523 char *cmd = NULL;
1524
1525 strtok(args, " ");
1526 if ((cmd = strtok(NULL, " ")) == NULL) {
1527
1528generic_help:
1529 fprintf(stdout, "Available commands:\n");
1530
1531 for (i = 0; commands[i].name; i++) {
1532 if (commands[i].helpstring != NULL) {
1533 fprintf(stdout, " %-15s %s\n", commands[i].name, commands[i].helpstring);
1534 }
1535 }
1536 } else {
1537 /* print specific help for the selected command */
1538
1539 /* get the command of the specified name */
1540 for (i = 0; commands[i].name; i++) {
1541 if (strcmp(cmd, commands[i].name) == 0) {
1542 break;
1543 }
1544 }
1545
1546 /* execute the command's help if any valid command specified */
1547 if (commands[i].name) {
1548 if (commands[i].help_func != NULL) {
1549 commands[i].help_func();
1550 } else {
1551 printf("%s\n", commands[i].helpstring);
1552 }
1553 } else {
1554 /* if unknown command specified, print the list of commands */
1555 printf("Unknown command \'%s\'\n", cmd);
1556 goto generic_help;
1557 }
1558 }
1559
1560 free(args);
1561 return 0;
1562}
1563
1564COMMAND commands[] = {
1565 {"help", cmd_help, NULL, "Display commands description"},
1566 {"add", cmd_add, cmd_add_help, "Add a new model from a specific file"},
1567 {"load", cmd_load, cmd_load_help, "Load a new model from the searchdirs"},
1568 {"print", cmd_print, cmd_print_help, "Print a model"},
Radek Krejcied5acc52019-04-25 15:57:04 +02001569 {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
Radek Krejcie7b95092019-05-15 11:03:07 +02001570#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001571 {"xpath", cmd_xpath, cmd_xpath_help, "Get data nodes satisfying an XPath expression"},
1572 {"list", cmd_list, cmd_list_help, "List all the loaded models"},
1573#endif
1574 {"feature", cmd_feature, cmd_feature_help, "Print/enable/disable all/specific features of models"},
1575 {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for models"},
1576 {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded models"},
1577 {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
1578#ifndef NDEBUG
1579 {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
1580#endif
1581 {"quit", cmd_quit, NULL, "Quit the program"},
1582 /* synonyms for previous commands */
1583 {"?", cmd_help, NULL, "Display commands description"},
1584 {"exit", cmd_quit, NULL, "Quit the program"},
1585 {NULL, NULL, NULL, NULL}
1586};