blob: 0e7913aaa874048ea1ca0b63f1f6ec19e8433047 [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{
349 int c, argc, option_index, ret = 1, tree_ll = 0, tree_opts = 0;
350 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[] = {
356 {"help", no_argument, 0, 'h'},
357 {"format", required_argument, 0, 'f'},
358 {"output", required_argument, 0, 'o'},
359#if 0
360 {"tree-print-groupings", no_argument, 0, 'g'},
361 {"tree-print-uses", no_argument, 0, 'u'},
362 {"tree-no-leafref-target", no_argument, 0, 'n'},
363 {"tree-path", required_argument, 0, 'P'},
364 {"info-path", required_argument, 0, 'P'},
365 {"tree-line-length", required_argument, 0, 'L'},
366#endif
367 {NULL, 0, 0, 0}
368 };
369 void *rlcd;
370
371 argc = 1;
372 argv = malloc(2*sizeof *argv);
373 *argv = strdup(arg);
374 ptr = strtok(*argv, " ");
375 while ((ptr = strtok(NULL, " "))) {
376 rlcd = realloc(argv, (argc+2)*sizeof *argv);
377 if (!rlcd) {
378 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
379 goto cleanup;
380 }
381 argv = rlcd;
382 argv[argc++] = ptr;
383 }
384 argv[argc] = NULL;
385
386 optind = 0;
387 while (1) {
388 option_index = 0;
389 c = getopt_long(argc, argv, "hf:go:guP:L:", long_options, &option_index);
390 if (c == -1) {
391 break;
392 }
393
394 switch (c) {
395 case 'h':
396 cmd_print_help();
397 ret = 0;
398 goto cleanup;
399 case 'f':
400 if (!strcmp(optarg, "yang")) {
401 format = LYS_OUT_YANG;
402#if 0
403 } else if (!strcmp(optarg, "yin")) {
404 format = LYS_OUT_YIN;
405 } else if (!strcmp(optarg, "tree")) {
406 format = LYS_OUT_TREE;
407 } else if (!strcmp(optarg, "tree-rfc")) {
408 format = LYS_OUT_TREE;
409 tree_opts |= LYS_OUTOPT_TREE_RFC;
410 } else if (!strcmp(optarg, "info")) {
411 format = LYS_OUT_INFO;
412 } else if (!strcmp(optarg, "jsons")) {
413 format = LYS_OUT_JSON;
414#endif
415 } else {
416 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
417 goto cleanup;
418 }
419 break;
420 case 'o':
421 if (out_path) {
422 fprintf(stderr, "Output specified twice.\n");
423 goto cleanup;
424 }
425 out_path = optarg;
426 break;
427#if 0
428 case 'g':
429 tree_opts |= LYS_OUTOPT_TREE_GROUPING;
430 break;
431 case 'u':
432 tree_opts |= LYS_OUTOPT_TREE_USES;
433 break;
434 case 'n':
435 tree_opts |= LYS_OUTOPT_TREE_NO_LEAFREF;
436 break;
437 case 'P':
438 target_path = optarg;
439 break;
440 case 'L':
441 tree_ll = atoi(optarg);
442 break;
443#endif
444 case '?':
445 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
446 goto cleanup;
447 }
448 }
449
450 /* file name */
451 if (optind == argc) {
452 fprintf(stderr, "Missing the module name.\n");
453 goto cleanup;
454 }
455
456 /* tree fromat with or without gropings */
457 if ((tree_opts || tree_ll) && format != LYS_OUT_TREE) {
458 fprintf(stderr, "--tree options take effect only in case of the tree output format.\n");
459 }
460
461 /* module, revision */
462 model_name = argv[optind];
463 revision = NULL;
464 if (strchr(model_name, '@')) {
465 revision = strchr(model_name, '@');
466 revision[0] = '\0';
467 ++revision;
468 }
469
470 if (revision) {
471 module = ly_ctx_get_module(ctx, model_name, revision);
472 } else {
473 module = ly_ctx_get_module_latest(ctx, model_name);
474 }
475#if 0
476 if (!module) {
477 /* not a module, try to find it as a submodule */
478 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
479 }
480#endif
481
482 if (!module) {
483 if (revision) {
484 fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
485 } else {
486 fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
487 }
488 goto cleanup;
489 }
490
491 if (out_path) {
492 output = fopen(out_path, "w");
493 if (!output) {
494 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
495 goto cleanup;
496 }
497 }
498
499 ret = lys_print_file(output, module, format, tree_ll, tree_opts);
500 if (format == LYS_OUT_JSON) {
501 fputs("\n", output);
502 }
503
504cleanup:
505 free(*argv);
506 free(argv);
507
508 if (output && (output != stdout)) {
509 fclose(output);
510 }
511
512 return ret;
513}
514#if 0
515static LYD_FORMAT
516detect_data_format(char *filepath)
517{
518 size_t len;
519
520 /* detect input format according to file suffix */
521 len = strlen(filepath);
522 for (; isspace(filepath[len - 1]); len--, filepath[len] = '\0'); /* remove trailing whitespaces */
523 if (len >= 5 && !strcmp(&filepath[len - 4], ".xml")) {
524 return LYD_XML;
525 } else if (len >= 6 && !strcmp(&filepath[len - 5], ".json")) {
526 return LYD_JSON;
527 } else if (len >= 5 && !strcmp(&filepath[len - 4], ".lyb")) {
528 return LYD_LYB;
529 } else {
530 return LYD_UNKNOWN;
531 }
532}
533
534static int
535parse_data(char *filepath, int *options, struct lyd_node *val_tree, const char *rpc_act_file,
536 struct lyd_node **result)
537{
538 LYD_FORMAT informat = LYD_UNKNOWN;
539 struct lyxml_elem *xml = NULL;
540 struct lyd_node *data = NULL, *rpc_act = NULL;
541 int opts = *options;
542
543 /* detect input format according to file suffix */
544 informat = detect_data_format(filepath);
545 if (informat == LYD_UNKNOWN) {
546 fprintf(stderr, "Unable to resolve format of the input file, please add \".xml\", \".json\", or \".lyb\" suffix.\n");
547 return EXIT_FAILURE;
548 }
549
550 ly_errno = LY_SUCCESS;
551
552 if ((opts & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
553 /* automatically detect data type from the data top level */
554 if (informat != LYD_XML) {
555 fprintf(stderr, "Only XML data can be automatically explored.\n");
556 return EXIT_FAILURE;
557 }
558
559 xml = lyxml_parse_path(ctx, filepath, 0);
560 if (!xml) {
561 fprintf(stderr, "Failed to parse XML data for automatic type detection.\n");
562 return EXIT_FAILURE;
563 }
564
565 /* NOTE: namespace is ignored to simplify usage of this feature */
566
567 if (!strcmp(xml->name, "data")) {
568 fprintf(stdout, "Parsing %s as complete datastore.\n", filepath);
569 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_ADD_YANGLIB;
570 } else if (!strcmp(xml->name, "config")) {
571 fprintf(stdout, "Parsing %s as config data.\n", filepath);
572 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
573 } else if (!strcmp(xml->name, "get-reply")) {
574 fprintf(stdout, "Parsing %s as <get> reply data.\n", filepath);
575 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
576 } else if (!strcmp(xml->name, "get-config-reply")) {
577 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", filepath);
578 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
579 } else if (!strcmp(xml->name, "edit-config")) {
580 fprintf(stdout, "Parsing %s as <edit-config> data.\n", filepath);
581 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
582 } else if (!strcmp(xml->name, "rpc")) {
583 fprintf(stdout, "Parsing %s as <rpc> data.\n", filepath);
584 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
585 } else if (!strcmp(xml->name, "rpc-reply")) {
586 if (!rpc_act_file) {
587 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
588 lyxml_free(ctx, xml);
589 return EXIT_FAILURE;
590 }
591 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", filepath);
592 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
593 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, val_tree);
594 if (!rpc_act) {
595 fprintf(stderr, "Failed to parse RPC/action.\n");
596 lyxml_free(ctx, xml);
597 return EXIT_FAILURE;
598 }
599 } else if (!strcmp(xml->name, "notification")) {
600 fprintf(stdout, "Parsing %s as <notification> data.\n", filepath);
601 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
602 } else if (!strcmp(xml->name, "yang-data")) {
603 fprintf(stdout, "Parsing %s as <yang-data> data.\n", filepath);
604 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
605 if (!rpc_act_file) {
606 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
607 lyxml_free(ctx, xml);
608 return EXIT_FAILURE;
609 }
610 } else {
611 fprintf(stderr, "Invalid top-level element for automatic data type recognition.\n");
612 lyxml_free(ctx, xml);
613 return EXIT_FAILURE;
614 }
615
616 if (opts & LYD_OPT_RPCREPLY) {
617 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act, val_tree);
618 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
619 data = lyd_parse_xml(ctx, &xml->child, opts, val_tree);
620 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
621 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act_file);
622 } else {
623 data = lyd_parse_xml(ctx, &xml->child, opts);
624 }
625 lyxml_free(ctx, xml);
626 } else {
627 if (opts & LYD_OPT_RPCREPLY) {
628 if (!rpc_act_file) {
629 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
630 return EXIT_FAILURE;
631 }
632 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, val_tree);
633 if (!rpc_act) {
634 fprintf(stderr, "Failed to parse RPC/action.\n");
635 return EXIT_FAILURE;
636 }
637 data = lyd_parse_path(ctx, filepath, informat, opts, rpc_act, val_tree);
638 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
639 data = lyd_parse_path(ctx, filepath, informat, opts, val_tree);
640 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
641 if (!rpc_act_file) {
642 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
643 return EXIT_FAILURE;
644 }
645 data = lyd_parse_path(ctx, filepath, informat, opts, rpc_act_file);
646 } else {
647 if (!(opts & LYD_OPT_TYPEMASK)) {
648 /* automatically add yang-library data */
649 opts |= LYD_OPT_DATA_ADD_YANGLIB;
650 }
651 data = lyd_parse_path(ctx, filepath, informat, opts);
652 }
653 }
654 lyd_free_withsiblings(rpc_act);
655
656 if (ly_errno) {
657 fprintf(stderr, "Failed to parse data.\n");
658 lyd_free_withsiblings(data);
659 return EXIT_FAILURE;
660 }
661
662 *result = data;
663 *options = opts;
664 return EXIT_SUCCESS;
665}
666
667int
668cmd_data(const char *arg)
669{
670 int c, argc, option_index, ret = 1;
671 int options = 0, printopt = 0;
672 char **argv = NULL, *ptr;
673 const char *out_path = NULL;
674 struct lyd_node *data = NULL, *val_tree = NULL;
675 LYD_FORMAT outformat = LYD_UNKNOWN;
676 FILE *output = stdout;
677 static struct option long_options[] = {
678 {"defaults", required_argument, 0, 'd'},
679 {"help", no_argument, 0, 'h'},
680 {"format", required_argument, 0, 'f'},
681 {"option", required_argument, 0, 't'},
682 {"output", required_argument, 0, 'o'},
683 {"running", required_argument, 0, 'r'},
684 {"strict", no_argument, 0, 's'},
685 {NULL, 0, 0, 0}
686 };
687 void *rlcd;
688
689 argc = 1;
690 argv = malloc(2*sizeof *argv);
691 *argv = strdup(arg);
692 ptr = strtok(*argv, " ");
693 while ((ptr = strtok(NULL, " "))) {
694 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
695 if (!rlcd) {
696 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
697 goto cleanup;
698 }
699 argv = rlcd;
700 argv[argc++] = ptr;
701 }
702 argv[argc] = NULL;
703
704 optind = 0;
705 while (1) {
706 option_index = 0;
707 c = getopt_long(argc, argv, "d:hf:o:st:r:", long_options, &option_index);
708 if (c == -1) {
709 break;
710 }
711
712 switch (c) {
713 case 'd':
714 if (!strcmp(optarg, "all")) {
715 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL;
716 } else if (!strcmp(optarg, "all-tagged")) {
717 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
718 } else if (!strcmp(optarg, "trim")) {
719 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_TRIM;
720 } else if (!strcmp(optarg, "implicit-tagged")) {
721 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
722 }
723 break;
724 case 'h':
725 cmd_data_help();
726 ret = 0;
727 goto cleanup;
728 case 'f':
729 if (!strcmp(optarg, "xml")) {
730 outformat = LYD_XML;
731 } else if (!strcmp(optarg, "json")) {
732 outformat = LYD_JSON;
733 } else if (!strcmp(optarg, "lyb")) {
734 outformat = LYD_LYB;
735 } else {
736 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
737 goto cleanup;
738 }
739 break;
740 case 'o':
741 if (out_path) {
742 fprintf(stderr, "Output specified twice.\n");
743 goto cleanup;
744 }
745 out_path = optarg;
746 break;
747 case 'r':
748 if (val_tree || (options & LYD_OPT_NOEXTDEPS)) {
749 fprintf(stderr, "The running datastore (-r) cannot be set multiple times.\n");
750 goto cleanup;
751 }
752 if (optarg[0] == '!') {
753 /* ignore extenral dependencies to the running datastore */
754 options |= LYD_OPT_NOEXTDEPS;
755 } else {
756 /* external file with the running datastore */
757 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_DATA_NO_YANGLIB);
758 if (!val_tree) {
759 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
760 goto cleanup;
761 }
762 }
763 break;
764 case 's':
765 options |= LYD_OPT_STRICT;
766 options |= LYD_OPT_OBSOLETE;
767 break;
768 case 't':
769 if (!strcmp(optarg, "auto")) {
770 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
771 } else if (!strcmp(optarg, "data")) {
772 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA;
773 } else if (!strcmp(optarg, "config")) {
774 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
775 } else if (!strcmp(optarg, "get")) {
776 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
777 } else if (!strcmp(optarg, "getconfig")) {
778 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
779 } else if (!strcmp(optarg, "edit")) {
780 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
781 } else if (!strcmp(optarg, "rpc")) {
782 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
783 } else if (!strcmp(optarg, "rpcreply")) {
784 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
785 } else if (!strcmp(optarg, "notif")) {
786 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
787 } else if (!strcmp(optarg, "yangdata")) {
788 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
789 } else {
790 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
791 cmd_data_help();
792 goto cleanup;
793 }
794 break;
795 case '?':
796 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
797 goto cleanup;
798 }
799 }
800
801 /* file name */
802 if (optind == argc) {
803 fprintf(stderr, "Missing the data file name.\n");
804 goto cleanup;
805 }
806
807 if (parse_data(argv[optind], &options, val_tree, argv[optind + 1], &data)) {
808 goto cleanup;
809 }
810
811 if (out_path) {
812 output = fopen(out_path, "w");
813 if (!output) {
814 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
815 goto cleanup;
816 }
817 }
818
819 if (outformat != LYD_UNKNOWN) {
820 if (options & LYD_OPT_RPCREPLY) {
821 lyd_print_file(output, data->child, outformat, LYP_WITHSIBLINGS | LYP_FORMAT | printopt);
822 } else {
823 lyd_print_file(output, data, outformat, LYP_WITHSIBLINGS | LYP_FORMAT | printopt);
824 }
825 }
826
827 ret = 0;
828
829cleanup:
830 free(*argv);
831 free(argv);
832
833 if (output && (output != stdout)) {
834 fclose(output);
835 }
836
837 lyd_free_withsiblings(val_tree);
838 lyd_free_withsiblings(data);
839
840 return ret;
841}
842
843int
844cmd_xpath(const char *arg)
845{
846 int c, argc, option_index, ret = 1, long_str;
847 char **argv = NULL, *ptr, *expr = NULL;
848 unsigned int i, j;
849 int options = 0;
850 struct lyd_node *data = NULL, *node, *val_tree = NULL;
851 struct lyd_node_leaf_list *key;
852 struct ly_set *set;
853 static struct option long_options[] = {
854 {"help", no_argument, 0, 'h'},
855 {"expr", required_argument, 0, 'e'},
856 {NULL, 0, 0, 0}
857 };
858 void *rlcd;
859
860 long_str = 0;
861 argc = 1;
862 argv = malloc(2 * sizeof *argv);
863 *argv = strdup(arg);
864 ptr = strtok(*argv, " ");
865 while ((ptr = strtok(NULL, " "))) {
866 if (long_str) {
867 ptr[-1] = ' ';
868 if (ptr[strlen(ptr) - 1] == long_str) {
869 long_str = 0;
870 ptr[strlen(ptr) - 1] = '\0';
871 }
872 } else {
873 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
874 if (!rlcd) {
875 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
876 goto cleanup;
877 }
878 argv = rlcd;
879 argv[argc] = ptr;
880 if (ptr[0] == '"') {
881 long_str = '"';
882 ++argv[argc];
883 }
884 if (ptr[0] == '\'') {
885 long_str = '\'';
886 ++argv[argc];
887 }
888 if (ptr[strlen(ptr) - 1] == long_str) {
889 long_str = 0;
890 ptr[strlen(ptr) - 1] = '\0';
891 }
892 ++argc;
893 }
894 }
895 argv[argc] = NULL;
896
897 optind = 0;
898 while (1) {
899 option_index = 0;
900 c = getopt_long(argc, argv, "he:t:x:", long_options, &option_index);
901 if (c == -1) {
902 break;
903 }
904
905 switch (c) {
906 case 'h':
907 cmd_xpath_help();
908 ret = 0;
909 goto cleanup;
910 case 'e':
911 expr = optarg;
912 break;
913 case 't':
914 if (!strcmp(optarg, "auto")) {
915 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
916 } else if (!strcmp(optarg, "config")) {
917 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
918 } else if (!strcmp(optarg, "get")) {
919 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
920 } else if (!strcmp(optarg, "getconfig")) {
921 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
922 } else if (!strcmp(optarg, "edit")) {
923 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
924 } else if (!strcmp(optarg, "rpc")) {
925 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
926 } else if (!strcmp(optarg, "rpcreply")) {
927 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
928 } else if (!strcmp(optarg, "notif")) {
929 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
930 } else if (!strcmp(optarg, "yangdata")) {
931 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
932 } else {
933 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
934 cmd_data_help();
935 goto cleanup;
936 }
937 break;
938 case 'x':
939 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_CONFIG);
940 if (!val_tree) {
941 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
942 goto cleanup;
943 }
944 break;
945 case '?':
946 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
947 goto cleanup;
948 }
949 }
950
951 if (optind == argc) {
952 fprintf(stderr, "Missing the file with data.\n");
953 goto cleanup;
954 }
955
956 if (!expr) {
957 fprintf(stderr, "Missing the XPath expression.\n");
958 goto cleanup;
959 }
960
961 if (parse_data(argv[optind], &options, val_tree, argv[optind + 1], &data)) {
962 goto cleanup;
963 }
964
965 if (!(set = lyd_find_path(data, expr))) {
966 goto cleanup;
967 }
968
969 /* print result */
970 printf("Result:\n");
971 if (!set->number) {
972 printf("\tEmpty\n");
973 } else {
974 for (i = 0; i < set->number; ++i) {
975 node = set->set.d[i];
976 switch (node->schema->nodetype) {
977 case LYS_CONTAINER:
978 printf("\tContainer ");
979 break;
980 case LYS_LEAF:
981 printf("\tLeaf ");
982 break;
983 case LYS_LEAFLIST:
984 printf("\tLeaflist ");
985 break;
986 case LYS_LIST:
987 printf("\tList ");
988 break;
989 case LYS_ANYXML:
990 printf("\tAnyxml ");
991 break;
992 case LYS_ANYDATA:
993 printf("\tAnydata ");
994 break;
995 default:
996 printf("\tUnknown ");
997 break;
998 }
999 printf("\"%s\"", node->schema->name);
1000 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
1001 printf(" (val: %s)", ((struct lyd_node_leaf_list *)node)->value_str);
1002 } else if (node->schema->nodetype == LYS_LIST) {
1003 key = (struct lyd_node_leaf_list *)node->child;
1004 printf(" (");
1005 for (j = 0; j < ((struct lys_node_list *)node->schema)->keys_size; ++j) {
1006 if (j) {
1007 printf(" ");
1008 }
1009 printf("\"%s\": %s", key->schema->name, key->value_str);
1010 key = (struct lyd_node_leaf_list *)key->next;
1011 }
1012 printf(")");
1013 }
1014 printf("\n");
1015 }
1016 }
1017 printf("\n");
1018
1019 ly_set_free(set);
1020 ret = 0;
1021
1022cleanup:
1023 free(*argv);
1024 free(argv);
1025
1026 lyd_free_withsiblings(data);
1027
1028 return ret;
1029}
1030
1031int
1032print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
1033{
1034 struct lyd_node *ylib;
1035 uint32_t idx = 0, has_modules = 0;
1036 uint8_t u;
1037 const struct lys_module *mod;
1038
1039 if (outformat != LYD_UNKNOWN) {
1040 ylib = ly_ctx_info(ctx);
1041 if (!ylib) {
1042 fprintf(stderr, "Getting context info (ietf-yang-library data) failed.\n");
1043 return 1;
1044 }
1045
1046 lyd_print_file(out, ylib, outformat, LYP_WITHSIBLINGS | LYP_FORMAT);
1047 lyd_free_withsiblings(ylib);
1048 return 0;
1049 }
1050
1051 /* iterate schemas in context and provide just the basic info */
1052 fprintf(out, "List of the loaded models:\n");
1053 while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
1054 has_modules++;
1055
1056 /* conformance print */
1057 if (mod->implemented) {
1058 fprintf(out, "\tI");
1059 } else {
1060 fprintf(out, "\ti");
1061 }
1062
1063 /* module print */
1064 fprintf(out, " %s", mod->name);
1065 if (mod->rev_size) {
1066 fprintf(out, "@%s", mod->rev[0].date);
1067 }
1068
1069 /* submodules print */
1070 if (mod->inc_size) {
1071 fprintf(out, " (");
1072 for (u = 0; u < mod->inc_size; u++) {
1073 fprintf(out, "%s%s", !u ? "" : ",", mod->inc[u].submodule->name);
1074 if (mod->inc[u].submodule->rev_size) {
1075 fprintf(out, "@%s", mod->inc[u].submodule->rev[0].date);
1076 }
1077 }
1078 fprintf(out, ")");
1079 }
1080
1081 /* finish the line */
1082 fprintf(out, "\n");
1083 }
1084
1085 if (!has_modules) {
1086 fprintf(out, "\t(none)\n");
1087 }
1088
1089 return 0;
1090}
1091
1092int
1093cmd_list(const char *arg)
1094{
1095 char **argv = NULL, *ptr;
1096 int c, argc, option_index;
1097 LYD_FORMAT outformat = LYD_UNKNOWN;
1098 static struct option long_options[] = {
1099 {"help", no_argument, 0, 'h'},
1100 {"format", required_argument, 0, 'f'},
1101 {NULL, 0, 0, 0}
1102 };
1103 void *rlcd;
1104
1105 argc = 1;
1106 argv = malloc(2*sizeof *argv);
1107 *argv = strdup(arg);
1108 ptr = strtok(*argv, " ");
1109 while ((ptr = strtok(NULL, " "))) {
1110 rlcd = realloc(argv, (argc+2)*sizeof *argv);
1111 if (!rlcd) {
1112 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1113 goto error;
1114 }
1115 argv = rlcd;
1116 argv[argc++] = ptr;
1117 }
1118 argv[argc] = NULL;
1119
1120 optind = 0;
1121 while (1) {
1122 option_index = 0;
1123 c = getopt_long(argc, argv, "hf:", long_options, &option_index);
1124 if (c == -1) {
1125 break;
1126 }
1127
1128 switch (c) {
1129 case 'h':
1130 cmd_data_help();
1131 free(*argv);
1132 free(argv);
1133 return 0;
1134 case 'f':
1135 if (!strcmp(optarg, "xml")) {
1136 outformat = LYD_XML;
1137 } else if (!strcmp(optarg, "json")) {
1138 outformat = LYD_JSON;
1139 } else {
1140 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
1141 goto error;
1142 }
1143 break;
1144 case '?':
1145 /* getopt_long() prints message */
1146 goto error;
1147 }
1148 }
1149 if (optind != argc) {
1150 fprintf(stderr, "Unknown parameter \"%s\"\n", argv[optind]);
1151error:
1152 free(*argv);
1153 free(argv);
1154 return 1;
1155 }
1156 free(*argv);
1157 free(argv);
1158
1159 return print_list(stdout, ctx, outformat);
1160}
1161#endif
1162int
1163cmd_feature(const char *arg)
1164{
1165 int c, argc, option_index, ret = 1, task = 0;
1166 char **argv = NULL, *ptr, *model_name, *revision, *feat_names = NULL;
1167 const struct lys_module *module;
1168 static struct option long_options[] = {
1169 {"help", no_argument, 0, 'h'},
1170 {"enable", required_argument, 0, 'e'},
1171 {"disable", required_argument, 0, 'd'},
1172 {NULL, 0, 0, 0}
1173 };
1174 void *rlcd;
1175
1176 argc = 1;
1177 argv = malloc(2*sizeof *argv);
1178 *argv = strdup(arg);
1179 ptr = strtok(*argv, " ");
1180 while ((ptr = strtok(NULL, " "))) {
1181 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
1182 if (!rlcd) {
1183 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1184 goto cleanup;
1185 }
1186 argv = rlcd;
1187 argv[argc++] = ptr;
1188 }
1189 argv[argc] = NULL;
1190
1191 optind = 0;
1192 while (1) {
1193 option_index = 0;
1194 c = getopt_long(argc, argv, "he:d:", long_options, &option_index);
1195 if (c == -1) {
1196 break;
1197 }
1198
1199 switch (c) {
1200 case 'h':
1201 cmd_feature_help();
1202 ret = 0;
1203 goto cleanup;
1204 case 'e':
1205 if (task) {
1206 fprintf(stderr, "Only one of enable or disable can be specified.\n");
1207 goto cleanup;
1208 }
1209 task = 1;
1210 feat_names = optarg;
1211 break;
1212 case 'd':
1213 if (task) {
1214 fprintf(stderr, "Only one of enable, or disable can be specified.\n");
1215 goto cleanup;
1216 }
1217 task = 2;
1218 feat_names = optarg;
1219 break;
1220 case '?':
1221 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
1222 goto cleanup;
1223 }
1224 }
1225
1226 /* module name */
1227 if (optind == argc) {
1228 fprintf(stderr, "Missing the module name.\n");
1229 goto cleanup;
1230 }
1231
1232 revision = NULL;
1233 model_name = argv[optind];
1234 if (strchr(model_name, '@')) {
1235 revision = strchr(model_name, '@');
1236 revision[0] = '\0';
1237 ++revision;
1238 }
1239
1240 module = ly_ctx_get_module(ctx, model_name, revision);
1241#if 0
1242 if (!module) {
1243 /* not a module, try to find it as a submodule */
1244 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
1245 }
1246#endif
1247
1248 if (module == NULL) {
1249 if (revision) {
1250 fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
1251 } else {
1252 fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
1253 }
1254 goto cleanup;
1255 }
1256
1257 if (!task) {
1258 unsigned int len, max_len = 0;
1259 unsigned int u;
1260 struct lysc_feature *features;
1261
1262 printf("%s features:\n", module->name);
1263
1264 if (module->compiled) {
1265 features = module->compiled->features;
1266 } else {
1267 features = module->off_features;
1268 }
1269
1270 /* get the max len */
1271 LY_ARRAY_FOR(features, u) {
1272 len = strlen(features[u].name);
1273 if (len > max_len) {
1274 max_len = len;
1275 }
1276 }
1277
1278 LY_ARRAY_FOR(features, u) {
1279 printf("\t%-*s (%s)\n", max_len, features[u].name, (features[u].flags & LYS_FENABLED) ? "on" : "off");
1280 }
1281 if (!u) {
1282 printf("\t(none)\n");
1283 }
1284 } else {
1285 feat_names = strtok(feat_names, ",");
1286 while (feat_names) {
1287 if (((task == 1) && lys_feature_enable(module, feat_names))
1288 || ((task == 2) && lys_feature_disable(module, feat_names))) {
1289 fprintf(stderr, "Feature \"%s\" not found.\n", feat_names);
1290 ret = 1;
1291 }
1292 feat_names = strtok(NULL, ",");
1293 }
1294 }
1295
1296cleanup:
1297 free(*argv);
1298 free(argv);
1299
1300 return ret;
1301}
1302
1303int
1304cmd_searchpath(const char *arg)
1305{
1306 const char *path;
1307 const char * const *searchpaths;
1308 int index;
1309 struct stat st;
1310
1311 for (path = strchr(arg, ' '); path && (path[0] == ' '); ++path);
1312 if (!path || (path[0] == '\0')) {
1313 searchpaths = ly_ctx_get_searchdirs(ctx);
1314 if (searchpaths) {
1315 for (index = 0; searchpaths[index]; index++) {
1316 fprintf(stdout, "%s\n", searchpaths[index]);
1317 }
1318 }
1319 return 0;
1320 }
1321
1322 if ((!strncmp(path, "-h", 2) && (path[2] == '\0' || path[2] == ' ')) ||
1323 (!strncmp(path, "--help", 6) && (path[6] == '\0' || path[6] == ' '))) {
1324 cmd_searchpath_help();
1325 return 0;
1326 } else if (!strncmp(path, "--clear", 7) && (path[7] == '\0' || path[7] == ' ')) {
1327 ly_ctx_unset_searchdirs(ctx, NULL);
1328 return 0;
1329 }
1330
1331 if (stat(path, &st) == -1) {
1332 fprintf(stderr, "Failed to stat the search path (%s).\n", strerror(errno));
1333 return 1;
1334 }
1335 if (!S_ISDIR(st.st_mode)) {
1336 fprintf(stderr, "\"%s\" is not a directory.\n", path);
1337 return 1;
1338 }
1339
1340 ly_ctx_set_searchdir(ctx, path);
1341
1342 return 0;
1343}
1344
1345int
1346cmd_clear(const char *arg)
1347{
1348 struct ly_ctx *ctx_new;
1349 int options = 0;
1350#if 0
1351 int i;
1352 char *ylpath;
1353 const char * const *searchpaths;
1354 LYD_FORMAT format;
1355
1356 /* get optional yang library file name */
1357 for (i = 5; arg[i] && isspace(arg[i]); i++);
1358 if (arg[i]) {
1359 if (arg[i] == '-' && arg[i + 1] == 'e') {
1360 options = LY_CTX_NOYANGLIBRARY;
1361 goto create_empty;
1362 } else {
1363 ylpath = strdup(&arg[i]);
1364 format = detect_data_format(ylpath);
1365 if (format == LYD_UNKNOWN) {
1366 free(ylpath);
1367 fprintf(stderr, "Unable to resolve format of the yang library file, please add \".xml\" or \".json\" suffix.\n");
1368 goto create_empty;
1369 }
1370 searchpaths = ly_ctx_get_searchdirs(ctx);
1371 ctx_new = ly_ctx_new_ylpath(searchpaths ? searchpaths[0] : NULL, ylpath, format, 0);
1372 free(ylpath);
1373 }
1374 } else {
1375create_empty:
1376#else
1377 (void) arg; /* TODO unused */
1378 {
1379#endif
1380 ly_ctx_new(NULL, options, &ctx_new);
1381 }
1382
1383 if (!ctx_new) {
1384 fprintf(stderr, "Failed to create context.\n");
1385 return 1;
1386 }
1387
1388 /* final switch */
1389 ly_ctx_destroy(ctx, NULL);
1390 ctx = ctx_new;
1391
1392 return 0;
1393}
1394
1395int
1396cmd_verb(const char *arg)
1397{
1398 const char *verb;
1399 if (strlen(arg) < 5) {
1400 cmd_verb_help();
1401 return 1;
1402 }
1403
1404 verb = arg + 5;
1405 if (!strcmp(verb, "error") || !strcmp(verb, "0")) {
1406 ly_verb(LY_LLERR);
1407#ifndef NDEBUG
1408 ly_verb_dbg(0);
1409#endif
1410 } else if (!strcmp(verb, "warning") || !strcmp(verb, "1")) {
1411 ly_verb(LY_LLWRN);
1412#ifndef NDEBUG
1413 ly_verb_dbg(0);
1414#endif
1415 } else if (!strcmp(verb, "verbose") || !strcmp(verb, "2")) {
1416 ly_verb(LY_LLVRB);
1417#ifndef NDEBUG
1418 ly_verb_dbg(0);
1419#endif
1420 } else if (!strcmp(verb, "debug") || !strcmp(verb, "3")) {
1421 ly_verb(LY_LLDBG);
1422#ifndef NDEBUG
1423 ly_verb_dbg(LY_LDGDICT | LY_LDGYANG | LY_LDGYIN | LY_LDGXPATH | LY_LDGDIFF);
1424#endif
1425 } else {
1426 fprintf(stderr, "Unknown verbosity \"%s\"\n", verb);
1427 return 1;
1428 }
1429
1430 return 0;
1431}
1432
1433#ifndef NDEBUG
1434
1435int
1436cmd_debug(const char *arg)
1437{
1438 const char *beg, *end;
1439 int grps = 0;
1440 if (strlen(arg) < 6) {
1441 cmd_debug_help();
1442 return 1;
1443 }
1444
1445 end = arg + 6;
1446 while (end[0]) {
1447 for (beg = end; isspace(beg[0]); ++beg);
1448 if (!beg[0]) {
1449 break;
1450 }
1451
1452 for (end = beg; (end[0] && !isspace(end[0])); ++end);
1453
1454 if (!strncmp(beg, "dict", end - beg)) {
1455 grps |= LY_LDGDICT;
1456 } else if (!strncmp(beg, "yang", end - beg)) {
1457 grps |= LY_LDGYANG;
1458 } else if (!strncmp(beg, "yin", end - beg)) {
1459 grps |= LY_LDGYIN;
1460 } else if (!strncmp(beg, "xpath", end - beg)) {
1461 grps |= LY_LDGXPATH;
1462 } else if (!strncmp(beg, "diff", end - beg)) {
1463 grps |= LY_LDGDIFF;
1464 } else {
1465 fprintf(stderr, "Unknown debug group \"%.*s\"\n", (int)(end - beg), beg);
1466 return 1;
1467 }
1468 }
1469 ly_verb_dbg(grps);
1470
1471 return 0;
1472}
1473
1474#endif
1475
1476int
1477cmd_quit(const char *UNUSED(arg))
1478{
1479 done = 1;
1480 return 0;
1481}
1482
1483int
1484cmd_help(const char *arg)
1485{
1486 int i;
1487 char *args = strdup(arg);
1488 char *cmd = NULL;
1489
1490 strtok(args, " ");
1491 if ((cmd = strtok(NULL, " ")) == NULL) {
1492
1493generic_help:
1494 fprintf(stdout, "Available commands:\n");
1495
1496 for (i = 0; commands[i].name; i++) {
1497 if (commands[i].helpstring != NULL) {
1498 fprintf(stdout, " %-15s %s\n", commands[i].name, commands[i].helpstring);
1499 }
1500 }
1501 } else {
1502 /* print specific help for the selected command */
1503
1504 /* get the command of the specified name */
1505 for (i = 0; commands[i].name; i++) {
1506 if (strcmp(cmd, commands[i].name) == 0) {
1507 break;
1508 }
1509 }
1510
1511 /* execute the command's help if any valid command specified */
1512 if (commands[i].name) {
1513 if (commands[i].help_func != NULL) {
1514 commands[i].help_func();
1515 } else {
1516 printf("%s\n", commands[i].helpstring);
1517 }
1518 } else {
1519 /* if unknown command specified, print the list of commands */
1520 printf("Unknown command \'%s\'\n", cmd);
1521 goto generic_help;
1522 }
1523 }
1524
1525 free(args);
1526 return 0;
1527}
1528
1529COMMAND commands[] = {
1530 {"help", cmd_help, NULL, "Display commands description"},
1531 {"add", cmd_add, cmd_add_help, "Add a new model from a specific file"},
1532 {"load", cmd_load, cmd_load_help, "Load a new model from the searchdirs"},
1533 {"print", cmd_print, cmd_print_help, "Print a model"},
1534#if 0
1535 {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
1536 {"xpath", cmd_xpath, cmd_xpath_help, "Get data nodes satisfying an XPath expression"},
1537 {"list", cmd_list, cmd_list_help, "List all the loaded models"},
1538#endif
1539 {"feature", cmd_feature, cmd_feature_help, "Print/enable/disable all/specific features of models"},
1540 {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for models"},
1541 {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded models"},
1542 {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
1543#ifndef NDEBUG
1544 {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
1545#endif
1546 {"quit", cmd_quit, NULL, "Quit the program"},
1547 /* synonyms for previous commands */
1548 {"?", cmd_help, NULL, "Display commands description"},
1549 {"exit", cmd_quit, NULL, "Quit the program"},
1550 {NULL, NULL, NULL, NULL}
1551};