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