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