blob: d654579c653659630cfe785cb00996f22230c6fc [file] [log] [blame]
Radek Krejcied5acc52019-04-25 15:57:04 +02001/**
2 * @file commands.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libyang's yanglint tool commands
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
Radek Krejcied5acc52019-04-25 15:57:04 +020016
17#include "commands.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020018
19#include <ctype.h>
20#include <errno.h>
21#include <getopt.h>
22#include <libgen.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27
Michal Vasko5aa44c02020-06-29 11:47:02 +020028#include "compat.h"
Radek Krejcied5acc52019-04-25 15:57:04 +020029#include "libyang.h"
30
31COMMAND commands[];
32extern int done;
33extern struct ly_ctx *ctx;
34
35void
36cmd_add_help(void)
37{
38 printf("add [-i] <path-to-model> [<paths-to-other-models> ...]\n");
39 printf("\t-i - make all the imported modules implemented\n");
40}
41
42void
43cmd_load_help(void)
44{
45 printf("load [-i] <model-name> [<other-model-names> ...]\n");
46 printf("\t-i - make all the imported modules implemented\n");
47}
48
49void
50cmd_clear_help(void)
51{
52 printf("clear [<yang-library> | -e]\n");
53 printf("\t Replace the current context with an empty one, searchpaths are not kept.\n");
54 printf("\t If <yang-library> path specified, load the modules according to the yang library data.\n");
55 printf("\t Option '-e' causes ietf-yang-library will not be loaded.\n");
56}
57
58void
59cmd_print_help(void)
60{
Radek Krejcid8c0f5e2019-11-17 12:18:34 +080061 printf("print [-f (yang | yin | tree [<tree-options>] | info [-P <info-path>] [-(-s)ingle-node])] [-o <output-file>]"
Radek Krejcied5acc52019-04-25 15:57:04 +020062 " <model-name>[@<revision>]\n");
63 printf("\n");
64 printf("\ttree-options:\t--tree-print-groupings\t(print top-level groupings in a separate section)\n");
65 printf("\t \t--tree-print-uses\t(print uses nodes instead the resolved grouping nodes)\n");
66 printf("\t \t--tree-no-leafref-target\t(do not print the target nodes of leafrefs)\n");
67 printf("\t \t--tree-path <schema-path>\t(print only the specified subtree)\n");
68 printf("\t \t--tree-line-length <line-length>\t(wrap lines if longer than line-length,\n");
69 printf("\t \t\tnot a strict limit, longer lines can often appear)\n");
70 printf("\n");
Radek Krejcid8c0f5e2019-11-17 12:18:34 +080071 printf("\tinfo-path:\t<schema-path> | identity/<identity-name> | feature/<feature-name>\n");
Radek Krejcied5acc52019-04-25 15:57:04 +020072 printf("\n");
73 printf("\tschema-path:\t( /<module-name>:<node-identifier> )+\n");
74}
75
76void
77cmd_data_help(void)
78{
79 printf("data [-(-s)trict] [-t TYPE] [-d DEFAULTS] [-o <output-file>] [-f (xml | json | lyb)] [-r <running-file-name>]\n");
80 printf(" <data-file-name> [<RPC/action-data-file-name> | <yang-data name>]\n\n");
81 printf("Accepted TYPEs:\n");
82 printf("\tauto - resolve data type (one of the following) automatically (as pyang does),\n");
83 printf("\t this option is applicable only in case of XML input data.\n");
84 printf("\tdata - LYD_OPT_DATA (default value) - complete datastore including status data.\n");
85 printf("\tconfig - LYD_OPT_CONFIG - complete configuration datastore.\n");
86 printf("\tget - LYD_OPT_GET - <get> operation result.\n");
87 printf("\tgetconfig - LYD_OPT_GETCONFIG - <get-config> operation result.\n");
88 printf("\tedit - LYD_OPT_EDIT - <edit-config>'s data (content of its <config> element).\n");
89 printf("\trpc - LYD_OPT_RPC - NETCONF RPC message.\n");
90 printf("\trpcreply - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
91 printf("\tnotif - LYD_OPT_NOTIF - NETCONF Notification message.\n");
92 printf("\tyangdata - LYD_OPT_DATA_TEMPLATE - yang-data extension (last parameter mandatory in this case)\n\n");
93 printf("Accepted DEFAULTS:\n");
94 printf("\tall - add missing default nodes\n");
95 printf("\tall-tagged - add missing default nodes and mark all the default nodes with the attribute.\n");
96 printf("\ttrim - remove all nodes with a default value\n");
97 printf("\timplicit-tagged - add missing nodes and mark them with the attribute\n\n");
98 printf("Option -r:\n");
99 printf("\tOptional parameter for 'rpc', 'rpcreply' and 'notif' TYPEs, the file contains running\n");
100 printf("\tconfiguration datastore data referenced from the RPC/Notification. Note that the file is\n");
101 printf("\tvalidated as 'data' TYPE. Special value '!' can be used as argument to ignore the\n");
102 printf("\texternal references.\n\n");
103 printf("\tIf an XPath expression (when/must) needs access to configuration data, you can provide\n");
104 printf("\tthem in a file, which will be parsed as 'data' TYPE.\n\n");
105}
106
107void
108cmd_xpath_help(void)
109{
110 printf("xpath [-t TYPE] [-x <additional-tree-file-name>] -e <XPath-expression>\n"
111 " <XML-data-file-name> [<JSON-rpc/action-schema-nodeid>]\n");
112 printf("Accepted TYPEs:\n");
113 printf("\tauto - resolve data type (one of the following) automatically (as pyang does),\n");
114 printf("\t this option is applicable only in case of XML input data.\n");
115 printf("\tconfig - LYD_OPT_CONFIG\n");
116 printf("\tget - LYD_OPT_GET\n");
117 printf("\tgetconfig - LYD_OPT_GETCONFIG\n");
118 printf("\tedit - LYD_OPT_EDIT\n");
119 printf("\trpc - LYD_OPT_RPC\n");
120 printf("\trpcreply - LYD_OPT_RPCREPLY (last parameter mandatory in this case)\n");
121 printf("\tnotif - LYD_OPT_NOTIF\n\n");
122 printf("Option -x:\n");
123 printf("\tIf RPC/action/notification/RPC reply (for TYPEs 'rpc', 'rpcreply', and 'notif') includes\n");
124 printf("\tan XPath expression (when/must) that needs access to the configuration data, you can provide\n");
125 printf("\tthem in a file, which will be parsed as 'config'.\n");
126}
127
128void
129cmd_list_help(void)
130{
131 printf("list [-f (xml | json)]\n\n");
132 printf("\tBasic list output (no -f): i - imported module, I - implemented module\n");
133}
134
135void
136cmd_feature_help(void)
137{
138 printf("feature [ -(-e)nable | -(-d)isable (* | <feature-name>[,<feature-name> ...]) ] <model-name>[@<revision>]\n");
139}
140
141void
142cmd_searchpath_help(void)
143{
144 printf("searchpath [<model-dir-path> | --clear]\n\n");
145 printf("\tThey are used to search for imports and includes of a model.\n");
146 printf("\tThe \"load\" command uses these directories to find models directly.\n");
147}
148
149void
150cmd_verb_help(void)
151{
152 printf("verb (error/0 | warning/1 | verbose/2 | debug/3)\n");
153}
154
155#ifndef NDEBUG
156
157void
158cmd_debug_help(void)
159{
160 printf("debug (dict | yang | yin | xpath | diff)+\n");
161}
162
163#endif
164
165LYS_INFORMAT
166get_schema_format(const char *path)
167{
168 char *ptr;
169
170 if ((ptr = strrchr(path, '.')) != NULL) {
171 ++ptr;
172 if (!strcmp(ptr, "yang")) {
173 return LYS_IN_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200174 } else if (!strcmp(ptr, "yin")) {
175 return LYS_IN_YIN;
Radek Krejcied5acc52019-04-25 15:57:04 +0200176 } else {
177 fprintf(stderr, "Input file in an unknown format \"%s\".\n", ptr);
178 return LYS_IN_UNKNOWN;
179 }
180 } else {
181 fprintf(stdout, "Input file \"%s\" without extension - unknown format.\n", path);
182 return LYS_IN_UNKNOWN;
183 }
184}
185
186int
187cmd_add(const char *arg)
188{
189 int path_len, ret = 1, index = 0;
190 char *path, *dir, *s, *arg_ptr;
191 const char * const *searchpaths;
192 const struct lys_module *model;
193 LYS_INFORMAT format = LYS_IN_UNKNOWN;
194
195 if (strlen(arg) < 5) {
196 cmd_add_help();
197 return 1;
198 }
199
200 arg_ptr = strdup(arg + 3 /* ignore "add" */);
201
202 for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
203 if (s[2] == '\0' || s[2] == ' ') {
Radek Krejci3fa46b62019-09-11 10:47:30 +0200204 ly_ctx_set_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200205 s[0] = s[1] = ' ';
206 }
207 }
208 s = arg_ptr;
209
210 while (arg_ptr[0] == ' ') {
211 ++arg_ptr;
212 }
213 if (strchr(arg_ptr, ' ')) {
214 path_len = strchr(arg_ptr, ' ') - arg_ptr;
215 } else {
216 path_len = strlen(arg_ptr);
217 }
218 path = strndup(arg_ptr, path_len);
219
220 searchpaths = ly_ctx_get_searchdirs(ctx);
221 if (searchpaths) {
222 for (index = 0; searchpaths[index]; index++);
223 }
224
225 while (path) {
226 format = get_schema_format(path);
227 if (format == LYS_IN_UNKNOWN) {
228 free(path);
229 goto cleanup;
230 }
231
232 dir = strdup(path);
233 ly_ctx_set_searchdir(ctx, dirname(dir));
Michal Vasko3a41dff2020-07-15 14:30:28 +0200234 lys_parse_path(ctx, path, format, &model);
Radek Krejcied5acc52019-04-25 15:57:04 +0200235 ly_ctx_unset_searchdir(ctx, index);
236 free(path);
237 free(dir);
238
239 if (!model) {
240 /* libyang printed the error messages */
241 goto cleanup;
242 }
243
244 /* next model */
245 arg_ptr += path_len;
246 while (arg_ptr[0] == ' ') {
247 ++arg_ptr;
248 }
249 if (strchr(arg_ptr, ' ')) {
250 path_len = strchr(arg_ptr, ' ') - arg_ptr;
251 } else {
252 path_len = strlen(arg_ptr);
253 }
254
255 if (path_len) {
256 path = strndup(arg_ptr, path_len);
257 } else {
258 path = NULL;
259 }
260 }
261 if (format == LYS_IN_UNKNOWN) {
262 /* no schema on input */
263 cmd_add_help();
264 goto cleanup;
265 }
266 ret = 0;
267
268cleanup:
269 free(s);
Radek Krejci3fa46b62019-09-11 10:47:30 +0200270 ly_ctx_unset_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200271
272 return ret;
273}
274
275int
276cmd_load(const char *arg)
277{
278 int name_len, ret = 1;
279 char *name, *s, *arg_ptr;
280 const struct lys_module *model;
281
282 if (strlen(arg) < 6) {
283 cmd_load_help();
284 return 1;
285 }
286
287 arg_ptr = strdup(arg + 4 /* ignore "load" */);
288
289 for (s = strstr(arg_ptr, "-i"); s ; s = strstr(s + 2, "-i")) {
290 if (s[2] == '\0' || s[2] == ' ') {
Radek Krejci3fa46b62019-09-11 10:47:30 +0200291 ly_ctx_set_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200292 s[0] = s[1] = ' ';
293 }
294 }
295 s = arg_ptr;
296
297 while (arg_ptr[0] == ' ') {
298 ++arg_ptr;
299 }
300 if (strchr(arg_ptr, ' ')) {
301 name_len = strchr(arg_ptr, ' ') - arg_ptr;
302 } else {
303 name_len = strlen(arg_ptr);
304 }
305 name = strndup(arg_ptr, name_len);
306
307 while (name) {
308 model = ly_ctx_load_module(ctx, name, NULL);
309 free(name);
310 if (!model) {
311 /* libyang printed the error messages */
312 goto cleanup;
313 }
314
315 /* next model */
316 arg_ptr += name_len;
317 while (arg_ptr[0] == ' ') {
318 ++arg_ptr;
319 }
320 if (strchr(arg_ptr, ' ')) {
321 name_len = strchr(arg_ptr, ' ') - arg_ptr;
322 } else {
323 name_len = strlen(arg_ptr);
324 }
325
326 if (name_len) {
327 name = strndup(arg_ptr, name_len);
328 } else {
329 name = NULL;
330 }
331 }
332 ret = 0;
333
334cleanup:
335 free(s);
Radek Krejci3fa46b62019-09-11 10:47:30 +0200336 ly_ctx_unset_options(ctx, LY_CTX_ALLIMPLEMENTED);
Radek Krejcied5acc52019-04-25 15:57:04 +0200337
338 return ret;
339}
340
341int
342cmd_print(const char *arg)
343{
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800344 int c, argc, option_index, ret = 1, tree_ll = 0, output_opts = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200345 char **argv = NULL, *ptr, *model_name, *revision;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800346 const char *out_path = NULL, *target_path = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200347 const struct lys_module *module;
348 LYS_OUTFORMAT format = LYS_OUT_TREE;
Radek Krejci241f6b52020-05-21 18:13:49 +0200349 struct ly_out *out = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200350 static struct option long_options[] = {
351 {"help", no_argument, 0, 'h'},
352 {"format", required_argument, 0, 'f'},
353 {"output", required_argument, 0, 'o'},
354#if 0
355 {"tree-print-groupings", no_argument, 0, 'g'},
356 {"tree-print-uses", no_argument, 0, 'u'},
357 {"tree-no-leafref-target", no_argument, 0, 'n'},
358 {"tree-path", required_argument, 0, 'P'},
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800359#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200360 {"info-path", required_argument, 0, 'P'},
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800361 {"single-node", no_argument, 0, 's'},
362#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200363 {"tree-line-length", required_argument, 0, 'L'},
364#endif
365 {NULL, 0, 0, 0}
366 };
367 void *rlcd;
368
369 argc = 1;
370 argv = malloc(2*sizeof *argv);
371 *argv = strdup(arg);
372 ptr = strtok(*argv, " ");
373 while ((ptr = strtok(NULL, " "))) {
374 rlcd = realloc(argv, (argc+2)*sizeof *argv);
375 if (!rlcd) {
376 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
377 goto cleanup;
378 }
379 argv = rlcd;
380 argv[argc++] = ptr;
381 }
382 argv[argc] = NULL;
383
384 optind = 0;
385 while (1) {
386 option_index = 0;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800387 c = getopt_long(argc, argv, "chf:go:guP:sL:", long_options, &option_index);
Radek Krejcied5acc52019-04-25 15:57:04 +0200388 if (c == -1) {
389 break;
390 }
391
392 switch (c) {
393 case 'h':
394 cmd_print_help();
395 ret = 0;
396 goto cleanup;
397 case 'f':
398 if (!strcmp(optarg, "yang")) {
399 format = LYS_OUT_YANG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200400 } else if (!strcmp(optarg, "yin")) {
401 format = LYS_OUT_YIN;
FredGand944bdc2019-11-05 21:57:07 +0800402#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200403 } else if (!strcmp(optarg, "tree")) {
404 format = LYS_OUT_TREE;
405 } else if (!strcmp(optarg, "tree-rfc")) {
406 format = LYS_OUT_TREE;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800407 output_opts |= LYS_OUTOPT_TREE_RFC;
Radek Krejcied5acc52019-04-25 15:57:04 +0200408#endif
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800409 } else if (!strcmp(optarg, "info")) {
410 format = LYS_OUT_YANG_COMPILED;
Radek Krejcied5acc52019-04-25 15:57:04 +0200411 } else {
412 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
413 goto cleanup;
414 }
415 break;
416 case 'o':
417 if (out_path) {
418 fprintf(stderr, "Output specified twice.\n");
419 goto cleanup;
420 }
421 out_path = optarg;
422 break;
423#if 0
424 case 'g':
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800425 output_opts |= LYS_OUTOPT_TREE_GROUPING;
Radek Krejcied5acc52019-04-25 15:57:04 +0200426 break;
427 case 'u':
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800428 output_opts |= LYS_OUTOPT_TREE_USES;
Radek Krejcied5acc52019-04-25 15:57:04 +0200429 break;
430 case 'n':
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800431 output_opts |= LYS_OUTOPT_TREE_NO_LEAFREF;
Radek Krejcied5acc52019-04-25 15:57:04 +0200432 break;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800433#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200434 case 'P':
435 target_path = optarg;
436 break;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800437 case 's':
Radek Krejci4fa6ebf2019-11-21 13:34:35 +0800438 output_opts |= LYS_OUTPUT_NO_SUBSTMT;
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800439 break;
440#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200441 case 'L':
442 tree_ll = atoi(optarg);
443 break;
444#endif
445 case '?':
446 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
447 goto cleanup;
448 }
449 }
450
451 /* file name */
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800452 if (optind == argc && !target_path) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200453 fprintf(stderr, "Missing the module name.\n");
454 goto cleanup;
455 }
456
Radek Krejci77954e82019-04-30 13:51:29 +0200457#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200458 /* tree fromat with or without gropings */
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800459 if ((output_opts || tree_ll) && format != LYS_OUT_TREE) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200460 fprintf(stderr, "--tree options take effect only in case of the tree output format.\n");
461 }
Radek Krejci77954e82019-04-30 13:51:29 +0200462#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200463
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800464 if (!target_path) {
465 /* module, revision */
466 model_name = argv[optind];
467 revision = NULL;
468 if (strchr(model_name, '@')) {
469 revision = strchr(model_name, '@');
470 revision[0] = '\0';
471 ++revision;
472 }
473
474 if (revision) {
475 module = ly_ctx_get_module(ctx, model_name, revision);
476 } else {
477 module = ly_ctx_get_module_latest(ctx, model_name);
478 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200479#if 0
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800480 if (!module) {
481 /* not a module, try to find it as a submodule */
482 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
483 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200484#endif
485
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800486 if (!module) {
487 if (revision) {
488 fprintf(stderr, "No (sub)module \"%s\" in revision %s found.\n", model_name, revision);
489 } else {
490 fprintf(stderr, "No (sub)module \"%s\" found.\n", model_name);
491 }
492 goto cleanup;
Radek Krejcied5acc52019-04-25 15:57:04 +0200493 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200494 }
495
496 if (out_path) {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200497 ret = ly_out_new_filepath(out_path, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100498 } else {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200499 ret = ly_out_new_file(stdout, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100500 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200501 if (ret) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100502 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
503 goto cleanup;
Radek Krejcied5acc52019-04-25 15:57:04 +0200504 }
505
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800506 if (target_path) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100507 const struct lysc_node *node = lys_find_node(ctx, NULL, target_path);
508 if (node) {
509 ret = lys_print_node(out, node, format, tree_ll, output_opts);
510 } else {
511 fprintf(stderr, "The requested schema node \"%s\" does not exists.\n", target_path);
512 }
Radek Krejcid8c0f5e2019-11-17 12:18:34 +0800513 } else {
Michal Vasko7c8439f2020-08-05 13:25:19 +0200514 ret = lys_print_module(out, module, format, tree_ll, output_opts);
Radek Krejcied5acc52019-04-25 15:57:04 +0200515 }
516
517cleanup:
518 free(*argv);
519 free(argv);
520
Radek Krejci241f6b52020-05-21 18:13:49 +0200521 ly_out_free(out, NULL, out_path ? 1 : 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200522
523 return ret;
524}
Radek Krejcie7b95092019-05-15 11:03:07 +0200525
Radek Krejcied5acc52019-04-25 15:57:04 +0200526static int
Michal Vaskof03ed032020-03-04 13:31:44 +0100527parse_data(char *filepath, int *options, const struct lyd_node *tree, const char *rpc_act_file,
Radek Krejcied5acc52019-04-25 15:57:04 +0200528 struct lyd_node **result)
529{
Radek Krejcied5acc52019-04-25 15:57:04 +0200530 struct lyd_node *data = NULL, *rpc_act = NULL;
531 int opts = *options;
Radek Krejci7931b192020-06-25 17:05:03 +0200532 struct ly_in *in;
Radek Krejcied5acc52019-04-25 15:57:04 +0200533
Radek Krejci7931b192020-06-25 17:05:03 +0200534 if (ly_in_new_filepath(filepath, 0, &in)) {
535 fprintf(stderr, "Unable to open input YANG data file \"%s\".", filepath);
Radek Krejcied5acc52019-04-25 15:57:04 +0200536 return EXIT_FAILURE;
537 }
538
Radek Krejcie7b95092019-05-15 11:03:07 +0200539#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200540 if ((opts & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
541 /* automatically detect data type from the data top level */
542 if (informat != LYD_XML) {
543 fprintf(stderr, "Only XML data can be automatically explored.\n");
544 return EXIT_FAILURE;
545 }
546
547 xml = lyxml_parse_path(ctx, filepath, 0);
548 if (!xml) {
549 fprintf(stderr, "Failed to parse XML data for automatic type detection.\n");
550 return EXIT_FAILURE;
551 }
552
553 /* NOTE: namespace is ignored to simplify usage of this feature */
554
555 if (!strcmp(xml->name, "data")) {
556 fprintf(stdout, "Parsing %s as complete datastore.\n", filepath);
557 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_ADD_YANGLIB;
558 } else if (!strcmp(xml->name, "config")) {
559 fprintf(stdout, "Parsing %s as config data.\n", filepath);
560 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
561 } else if (!strcmp(xml->name, "get-reply")) {
562 fprintf(stdout, "Parsing %s as <get> reply data.\n", filepath);
563 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
564 } else if (!strcmp(xml->name, "get-config-reply")) {
565 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", filepath);
566 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
567 } else if (!strcmp(xml->name, "edit-config")) {
568 fprintf(stdout, "Parsing %s as <edit-config> data.\n", filepath);
569 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
570 } else if (!strcmp(xml->name, "rpc")) {
571 fprintf(stdout, "Parsing %s as <rpc> data.\n", filepath);
572 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
573 } else if (!strcmp(xml->name, "rpc-reply")) {
574 if (!rpc_act_file) {
575 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
576 lyxml_free(ctx, xml);
577 return EXIT_FAILURE;
578 }
579 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", filepath);
580 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
581 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, val_tree);
582 if (!rpc_act) {
583 fprintf(stderr, "Failed to parse RPC/action.\n");
584 lyxml_free(ctx, xml);
585 return EXIT_FAILURE;
586 }
587 } else if (!strcmp(xml->name, "notification")) {
588 fprintf(stdout, "Parsing %s as <notification> data.\n", filepath);
589 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
590 } else if (!strcmp(xml->name, "yang-data")) {
591 fprintf(stdout, "Parsing %s as <yang-data> data.\n", filepath);
592 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
593 if (!rpc_act_file) {
594 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
595 lyxml_free(ctx, xml);
596 return EXIT_FAILURE;
597 }
598 } else {
599 fprintf(stderr, "Invalid top-level element for automatic data type recognition.\n");
600 lyxml_free(ctx, xml);
601 return EXIT_FAILURE;
602 }
603
604 if (opts & LYD_OPT_RPCREPLY) {
605 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act, val_tree);
606 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
607 data = lyd_parse_xml(ctx, &xml->child, opts, val_tree);
608 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
609 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act_file);
610 } else {
611 data = lyd_parse_xml(ctx, &xml->child, opts);
612 }
613 lyxml_free(ctx, xml);
614 } else {
615 if (opts & LYD_OPT_RPCREPLY) {
616 if (!rpc_act_file) {
617 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
618 return EXIT_FAILURE;
619 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200620 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200621 if (!rpc_act) {
622 fprintf(stderr, "Failed to parse RPC/action.\n");
623 return EXIT_FAILURE;
624 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200625 if (trees) {
626 const struct lyd_node **trees_new;
627 unsigned int u;
628 trees_new = lyd_trees_new(1, rpc_act);
629
630 LY_ARRAY_FOR(trees, u) {
631 trees_new = lyd_trees_add(trees_new, trees[u]);
632 }
633 lyd_trees_free(trees, 0);
634 trees = trees_new;
635 } else {
636 trees = lyd_trees_new(1, rpc_act);
637 }
638 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200639 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200640 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200641 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
642 if (!rpc_act_file) {
643 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
644 return EXIT_FAILURE;
645 }
646 data = lyd_parse_path(ctx, filepath, informat, opts, rpc_act_file);
647 } else {
Michal Vaskoa3881362020-01-21 15:57:35 +0100648#endif
Radek Krejci7931b192020-06-25 17:05:03 +0200649
Radek Krejci5536d282020-08-04 23:27:44 +0200650 lyd_parse_data(ctx, in, 0, opts, LYD_VALIDATE_PRESENT, &data);
Radek Krejcie7b95092019-05-15 11:03:07 +0200651#if 0
Michal Vaskoa3881362020-01-21 15:57:35 +0100652 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200653 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200654#endif
Radek Krejci7931b192020-06-25 17:05:03 +0200655 ly_in_free(in, 0);
656
Radek Krejcie7b95092019-05-15 11:03:07 +0200657 lyd_free_all(rpc_act);
Radek Krejcied5acc52019-04-25 15:57:04 +0200658
Radek Krejcie7b95092019-05-15 11:03:07 +0200659 if (ly_err_first(ctx)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200660 fprintf(stderr, "Failed to parse data.\n");
Radek Krejcie7b95092019-05-15 11:03:07 +0200661 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200662 return EXIT_FAILURE;
663 }
664
665 *result = data;
666 *options = opts;
667 return EXIT_SUCCESS;
668}
669
670int
671cmd_data(const char *arg)
672{
673 int c, argc, option_index, ret = 1;
674 int options = 0, printopt = 0;
675 char **argv = NULL, *ptr;
676 const char *out_path = NULL;
Michal Vaskof03ed032020-03-04 13:31:44 +0100677 struct lyd_node *data = NULL;
678 struct lyd_node *tree = NULL;
Michal Vasko52927e22020-03-16 17:26:14 +0100679 LYD_FORMAT outformat = 0;
Radek Krejci241f6b52020-05-21 18:13:49 +0200680 struct ly_out *out = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200681 static struct option long_options[] = {
682 {"defaults", required_argument, 0, 'd'},
683 {"help", no_argument, 0, 'h'},
684 {"format", required_argument, 0, 'f'},
685 {"option", required_argument, 0, 't'},
686 {"output", required_argument, 0, 'o'},
687 {"running", required_argument, 0, 'r'},
688 {"strict", no_argument, 0, 's'},
689 {NULL, 0, 0, 0}
690 };
691 void *rlcd;
692
693 argc = 1;
694 argv = malloc(2*sizeof *argv);
695 *argv = strdup(arg);
696 ptr = strtok(*argv, " ");
697 while ((ptr = strtok(NULL, " "))) {
698 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
699 if (!rlcd) {
700 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
701 goto cleanup;
702 }
703 argv = rlcd;
704 argv[argc++] = ptr;
705 }
706 argv[argc] = NULL;
707
708 optind = 0;
709 while (1) {
710 option_index = 0;
711 c = getopt_long(argc, argv, "d:hf:o:st:r:", long_options, &option_index);
712 if (c == -1) {
713 break;
714 }
715
716 switch (c) {
Radek Krejcie7b95092019-05-15 11:03:07 +0200717#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200718 case 'd':
719 if (!strcmp(optarg, "all")) {
720 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL;
721 } else if (!strcmp(optarg, "all-tagged")) {
722 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_ALL_TAG;
723 } else if (!strcmp(optarg, "trim")) {
724 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_TRIM;
725 } else if (!strcmp(optarg, "implicit-tagged")) {
726 printopt = (printopt & ~LYP_WD_MASK) | LYP_WD_IMPL_TAG;
727 }
728 break;
Radek Krejcie7b95092019-05-15 11:03:07 +0200729#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200730 case 'h':
731 cmd_data_help();
732 ret = 0;
733 goto cleanup;
734 case 'f':
735 if (!strcmp(optarg, "xml")) {
736 outformat = LYD_XML;
737 } else if (!strcmp(optarg, "json")) {
738 outformat = LYD_JSON;
739 } else if (!strcmp(optarg, "lyb")) {
740 outformat = LYD_LYB;
741 } else {
742 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
743 goto cleanup;
744 }
745 break;
746 case 'o':
747 if (out_path) {
748 fprintf(stderr, "Output specified twice.\n");
749 goto cleanup;
750 }
751 out_path = optarg;
752 break;
Michal Vaskoa3881362020-01-21 15:57:35 +0100753#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200754 case 'r':
Radek Krejcied5acc52019-04-25 15:57:04 +0200755 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 */
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200760 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_DATA_NO_YANGLIB, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200761 if (!val_tree) {
762 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
763 goto cleanup;
764 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200765 if (!trees) {
766 trees = lyd_trees_new(1, val_tree);
767 } else {
768 trees = lyd_trees_add(trees, val_tree);
769 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200770 }
771 break;
772 case 's':
773 options |= LYD_OPT_STRICT;
774 options |= LYD_OPT_OBSOLETE;
775 break;
Michal Vaskoa3881362020-01-21 15:57:35 +0100776#endif
Radek Krejcied5acc52019-04-25 15:57:04 +0200777 case 't':
778 if (!strcmp(optarg, "auto")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100779 /* no flags */
Radek Krejcied5acc52019-04-25 15:57:04 +0200780 } else if (!strcmp(optarg, "data")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100781 /* no flags */
Michal Vaskob36053d2020-03-26 15:49:30 +0100782 /*} else if (!strcmp(optarg, "config")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100783 options |= LYD_OPT_CONFIG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200784 } else if (!strcmp(optarg, "get")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100785 options |= LYD_OPT_GET;
Radek Krejcied5acc52019-04-25 15:57:04 +0200786 } else if (!strcmp(optarg, "getconfig")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100787 options |= LYD_OPT_GETCONFIG;
Michal Vaskob36053d2020-03-26 15:49:30 +0100788 } else if (!strcmp(optarg, "edit")) {
Michal Vasko9f96a052020-03-10 09:41:45 +0100789 options |= LYD_OPT_EDIT;*/
Radek Krejcied5acc52019-04-25 15:57:04 +0200790 } else {
791 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
792 cmd_data_help();
793 goto cleanup;
794 }
795 break;
796 case '?':
797 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
798 goto cleanup;
799 }
800 }
801
802 /* file name */
803 if (optind == argc) {
804 fprintf(stderr, "Missing the data file name.\n");
805 goto cleanup;
806 }
807
Michal Vaskof03ed032020-03-04 13:31:44 +0100808 if (parse_data(argv[optind], &options, tree, argv[optind + 1], &data)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200809 goto cleanup;
810 }
811
812 if (out_path) {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200813 ret = ly_out_new_filepath(out_path, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100814 } else {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200815 ret = ly_out_new_file(stdout, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100816 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200817 if (ret) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100818 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
819 goto cleanup;
Radek Krejcied5acc52019-04-25 15:57:04 +0200820 }
821
Michal Vasko52927e22020-03-16 17:26:14 +0100822 if (outformat) {
Michal Vasko3a41dff2020-07-15 14:30:28 +0200823 ret = lyd_print_all(out, data, outformat, LYD_PRINT_FORMAT | printopt);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200824 ret = ret < 0 ? ret * (-1) : 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200825 }
826
Radek Krejcied5acc52019-04-25 15:57:04 +0200827cleanup:
828 free(*argv);
829 free(argv);
830
Radek Krejci241f6b52020-05-21 18:13:49 +0200831 ly_out_free(out, NULL, out_path ? 1 : 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200832
Radek Krejcie7b95092019-05-15 11:03:07 +0200833 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200834
835 return ret;
836}
Radek Krejcie7b95092019-05-15 11:03:07 +0200837#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200838int
839cmd_xpath(const char *arg)
840{
841 int c, argc, option_index, ret = 1, long_str;
842 char **argv = NULL, *ptr, *expr = NULL;
843 unsigned int i, j;
844 int options = 0;
845 struct lyd_node *data = NULL, *node, *val_tree = NULL;
846 struct lyd_node_leaf_list *key;
847 struct ly_set *set;
848 static struct option long_options[] = {
849 {"help", no_argument, 0, 'h'},
850 {"expr", required_argument, 0, 'e'},
851 {NULL, 0, 0, 0}
852 };
853 void *rlcd;
854
855 long_str = 0;
856 argc = 1;
857 argv = malloc(2 * sizeof *argv);
858 *argv = strdup(arg);
859 ptr = strtok(*argv, " ");
860 while ((ptr = strtok(NULL, " "))) {
861 if (long_str) {
862 ptr[-1] = ' ';
863 if (ptr[strlen(ptr) - 1] == long_str) {
864 long_str = 0;
865 ptr[strlen(ptr) - 1] = '\0';
866 }
867 } else {
868 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
869 if (!rlcd) {
870 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
871 goto cleanup;
872 }
873 argv = rlcd;
874 argv[argc] = ptr;
875 if (ptr[0] == '"') {
876 long_str = '"';
877 ++argv[argc];
878 }
879 if (ptr[0] == '\'') {
880 long_str = '\'';
881 ++argv[argc];
882 }
883 if (ptr[strlen(ptr) - 1] == long_str) {
884 long_str = 0;
885 ptr[strlen(ptr) - 1] = '\0';
886 }
887 ++argc;
888 }
889 }
890 argv[argc] = NULL;
891
892 optind = 0;
893 while (1) {
894 option_index = 0;
895 c = getopt_long(argc, argv, "he:t:x:", long_options, &option_index);
896 if (c == -1) {
897 break;
898 }
899
900 switch (c) {
901 case 'h':
902 cmd_xpath_help();
903 ret = 0;
904 goto cleanup;
905 case 'e':
906 expr = optarg;
907 break;
908 case 't':
909 if (!strcmp(optarg, "auto")) {
910 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
911 } else if (!strcmp(optarg, "config")) {
912 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
913 } else if (!strcmp(optarg, "get")) {
914 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
915 } else if (!strcmp(optarg, "getconfig")) {
916 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
917 } else if (!strcmp(optarg, "edit")) {
918 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
919 } else if (!strcmp(optarg, "rpc")) {
920 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
921 } else if (!strcmp(optarg, "rpcreply")) {
922 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
923 } else if (!strcmp(optarg, "notif")) {
924 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
925 } else if (!strcmp(optarg, "yangdata")) {
926 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
927 } else {
928 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
929 cmd_data_help();
930 goto cleanup;
931 }
932 break;
933 case 'x':
934 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_CONFIG);
935 if (!val_tree) {
936 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
937 goto cleanup;
938 }
939 break;
940 case '?':
941 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
942 goto cleanup;
943 }
944 }
945
946 if (optind == argc) {
947 fprintf(stderr, "Missing the file with data.\n");
948 goto cleanup;
949 }
950
951 if (!expr) {
952 fprintf(stderr, "Missing the XPath expression.\n");
953 goto cleanup;
954 }
955
956 if (parse_data(argv[optind], &options, val_tree, argv[optind + 1], &data)) {
957 goto cleanup;
958 }
959
960 if (!(set = lyd_find_path(data, expr))) {
961 goto cleanup;
962 }
963
964 /* print result */
965 printf("Result:\n");
966 if (!set->number) {
967 printf("\tEmpty\n");
968 } else {
969 for (i = 0; i < set->number; ++i) {
970 node = set->set.d[i];
971 switch (node->schema->nodetype) {
972 case LYS_CONTAINER:
973 printf("\tContainer ");
974 break;
975 case LYS_LEAF:
976 printf("\tLeaf ");
977 break;
978 case LYS_LEAFLIST:
979 printf("\tLeaflist ");
980 break;
981 case LYS_LIST:
982 printf("\tList ");
983 break;
984 case LYS_ANYXML:
985 printf("\tAnyxml ");
986 break;
987 case LYS_ANYDATA:
988 printf("\tAnydata ");
989 break;
990 default:
991 printf("\tUnknown ");
992 break;
993 }
994 printf("\"%s\"", node->schema->name);
995 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
996 printf(" (val: %s)", ((struct lyd_node_leaf_list *)node)->value_str);
997 } else if (node->schema->nodetype == LYS_LIST) {
998 key = (struct lyd_node_leaf_list *)node->child;
999 printf(" (");
1000 for (j = 0; j < ((struct lys_node_list *)node->schema)->keys_size; ++j) {
1001 if (j) {
1002 printf(" ");
1003 }
1004 printf("\"%s\": %s", key->schema->name, key->value_str);
1005 key = (struct lyd_node_leaf_list *)key->next;
1006 }
1007 printf(")");
1008 }
1009 printf("\n");
1010 }
1011 }
1012 printf("\n");
1013
1014 ly_set_free(set);
1015 ret = 0;
1016
1017cleanup:
1018 free(*argv);
1019 free(argv);
1020
1021 lyd_free_withsiblings(data);
1022
1023 return ret;
1024}
1025
1026int
1027print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
1028{
1029 struct lyd_node *ylib;
1030 uint32_t idx = 0, has_modules = 0;
1031 uint8_t u;
1032 const struct lys_module *mod;
1033
1034 if (outformat != LYD_UNKNOWN) {
1035 ylib = ly_ctx_info(ctx);
1036 if (!ylib) {
1037 fprintf(stderr, "Getting context info (ietf-yang-library data) failed.\n");
1038 return 1;
1039 }
1040
1041 lyd_print_file(out, ylib, outformat, LYP_WITHSIBLINGS | LYP_FORMAT);
1042 lyd_free_withsiblings(ylib);
1043 return 0;
1044 }
1045
1046 /* iterate schemas in context and provide just the basic info */
1047 fprintf(out, "List of the loaded models:\n");
1048 while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
1049 has_modules++;
1050
1051 /* conformance print */
1052 if (mod->implemented) {
1053 fprintf(out, "\tI");
1054 } else {
1055 fprintf(out, "\ti");
1056 }
1057
1058 /* module print */
1059 fprintf(out, " %s", mod->name);
1060 if (mod->rev_size) {
1061 fprintf(out, "@%s", mod->rev[0].date);
1062 }
1063
1064 /* submodules print */
1065 if (mod->inc_size) {
1066 fprintf(out, " (");
1067 for (u = 0; u < mod->inc_size; u++) {
1068 fprintf(out, "%s%s", !u ? "" : ",", mod->inc[u].submodule->name);
1069 if (mod->inc[u].submodule->rev_size) {
1070 fprintf(out, "@%s", mod->inc[u].submodule->rev[0].date);
1071 }
1072 }
1073 fprintf(out, ")");
1074 }
1075
1076 /* finish the line */
1077 fprintf(out, "\n");
1078 }
1079
1080 if (!has_modules) {
1081 fprintf(out, "\t(none)\n");
1082 }
1083
1084 return 0;
1085}
1086
1087int
1088cmd_list(const char *arg)
1089{
1090 char **argv = NULL, *ptr;
1091 int c, argc, option_index;
1092 LYD_FORMAT outformat = LYD_UNKNOWN;
1093 static struct option long_options[] = {
1094 {"help", no_argument, 0, 'h'},
1095 {"format", required_argument, 0, 'f'},
1096 {NULL, 0, 0, 0}
1097 };
1098 void *rlcd;
1099
1100 argc = 1;
1101 argv = malloc(2*sizeof *argv);
1102 *argv = strdup(arg);
1103 ptr = strtok(*argv, " ");
1104 while ((ptr = strtok(NULL, " "))) {
1105 rlcd = realloc(argv, (argc+2)*sizeof *argv);
1106 if (!rlcd) {
1107 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1108 goto error;
1109 }
1110 argv = rlcd;
1111 argv[argc++] = ptr;
1112 }
1113 argv[argc] = NULL;
1114
1115 optind = 0;
1116 while (1) {
1117 option_index = 0;
1118 c = getopt_long(argc, argv, "hf:", long_options, &option_index);
1119 if (c == -1) {
1120 break;
1121 }
1122
1123 switch (c) {
1124 case 'h':
1125 cmd_data_help();
1126 free(*argv);
1127 free(argv);
1128 return 0;
1129 case 'f':
1130 if (!strcmp(optarg, "xml")) {
1131 outformat = LYD_XML;
1132 } else if (!strcmp(optarg, "json")) {
1133 outformat = LYD_JSON;
1134 } else {
1135 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
1136 goto error;
1137 }
1138 break;
1139 case '?':
1140 /* getopt_long() prints message */
1141 goto error;
1142 }
1143 }
1144 if (optind != argc) {
1145 fprintf(stderr, "Unknown parameter \"%s\"\n", argv[optind]);
1146error:
1147 free(*argv);
1148 free(argv);
1149 return 1;
1150 }
1151 free(*argv);
1152 free(argv);
1153
1154 return print_list(stdout, ctx, outformat);
1155}
1156#endif
1157int
1158cmd_feature(const char *arg)
1159{
1160 int c, argc, option_index, ret = 1, task = 0;
1161 char **argv = NULL, *ptr, *model_name, *revision, *feat_names = NULL;
1162 const struct lys_module *module;
1163 static struct option long_options[] = {
1164 {"help", no_argument, 0, 'h'},
1165 {"enable", required_argument, 0, 'e'},
1166 {"disable", required_argument, 0, 'd'},
1167 {NULL, 0, 0, 0}
1168 };
1169 void *rlcd;
1170
1171 argc = 1;
1172 argv = malloc(2*sizeof *argv);
1173 *argv = strdup(arg);
1174 ptr = strtok(*argv, " ");
1175 while ((ptr = strtok(NULL, " "))) {
1176 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
1177 if (!rlcd) {
1178 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1179 goto cleanup;
1180 }
1181 argv = rlcd;
1182 argv[argc++] = ptr;
1183 }
1184 argv[argc] = NULL;
1185
1186 optind = 0;
1187 while (1) {
1188 option_index = 0;
1189 c = getopt_long(argc, argv, "he:d:", long_options, &option_index);
1190 if (c == -1) {
1191 break;
1192 }
1193
1194 switch (c) {
1195 case 'h':
1196 cmd_feature_help();
1197 ret = 0;
1198 goto cleanup;
1199 case 'e':
1200 if (task) {
1201 fprintf(stderr, "Only one of enable or disable can be specified.\n");
1202 goto cleanup;
1203 }
1204 task = 1;
1205 feat_names = optarg;
1206 break;
1207 case 'd':
1208 if (task) {
1209 fprintf(stderr, "Only one of enable, or disable can be specified.\n");
1210 goto cleanup;
1211 }
1212 task = 2;
1213 feat_names = optarg;
1214 break;
1215 case '?':
1216 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
1217 goto cleanup;
1218 }
1219 }
1220
1221 /* module name */
1222 if (optind == argc) {
1223 fprintf(stderr, "Missing the module name.\n");
1224 goto cleanup;
1225 }
1226
1227 revision = NULL;
1228 model_name = argv[optind];
1229 if (strchr(model_name, '@')) {
1230 revision = strchr(model_name, '@');
1231 revision[0] = '\0';
1232 ++revision;
1233 }
1234
Radek Krejci3d46f612020-08-13 12:01:53 +02001235 if (!revision) {
1236 module = ly_ctx_get_module_implemented(ctx, model_name);
1237 } else {
1238 module = ly_ctx_get_module(ctx, model_name, revision);
1239 if (module && !module->implemented) {
1240 module = NULL;
1241 }
1242 }
Radek Krejcied5acc52019-04-25 15:57:04 +02001243#if 0
1244 if (!module) {
1245 /* not a module, try to find it as a submodule */
1246 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
1247 }
1248#endif
1249
1250 if (module == NULL) {
1251 if (revision) {
Radek Krejci3d46f612020-08-13 12:01:53 +02001252 fprintf(stderr, "No implemented (sub)module \"%s\" in revision %s found.\n", model_name, revision);
Radek Krejcied5acc52019-04-25 15:57:04 +02001253 } else {
Radek Krejci3d46f612020-08-13 12:01:53 +02001254 fprintf(stderr, "No implemented (sub)module \"%s\" found.\n", model_name);
Radek Krejcied5acc52019-04-25 15:57:04 +02001255 }
1256 goto cleanup;
1257 }
1258
1259 if (!task) {
Radek Krejci7eb54ba2020-05-18 16:30:04 +02001260 size_t len, max_len = 0;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02001261 LY_ARRAY_COUNT_TYPE u;
Radek Krejcied5acc52019-04-25 15:57:04 +02001262 struct lysc_feature *features;
1263
1264 printf("%s features:\n", module->name);
1265
1266 if (module->compiled) {
1267 features = module->compiled->features;
1268 } else {
Michal Vasko33ff9422020-07-03 09:50:39 +02001269 features = module->dis_features;
Radek Krejcied5acc52019-04-25 15:57:04 +02001270 }
1271
1272 /* get the max len */
1273 LY_ARRAY_FOR(features, u) {
1274 len = strlen(features[u].name);
1275 if (len > max_len) {
1276 max_len = len;
1277 }
1278 }
1279
1280 LY_ARRAY_FOR(features, u) {
Radek Krejci7eb54ba2020-05-18 16:30:04 +02001281 printf("\t%-*s (%s)\n", (int)max_len, features[u].name, (features[u].flags & LYS_FENABLED) ? "on" : "off");
Radek Krejcied5acc52019-04-25 15:57:04 +02001282 }
1283 if (!u) {
1284 printf("\t(none)\n");
1285 }
1286 } else {
1287 feat_names = strtok(feat_names, ",");
1288 while (feat_names) {
1289 if (((task == 1) && lys_feature_enable(module, feat_names))
1290 || ((task == 2) && lys_feature_disable(module, feat_names))) {
1291 fprintf(stderr, "Feature \"%s\" not found.\n", feat_names);
1292 ret = 1;
1293 }
1294 feat_names = strtok(NULL, ",");
1295 }
1296 }
1297
1298cleanup:
1299 free(*argv);
1300 free(argv);
1301
1302 return ret;
1303}
1304
1305int
1306cmd_searchpath(const char *arg)
1307{
1308 const char *path;
1309 const char * const *searchpaths;
1310 int index;
1311 struct stat st;
1312
1313 for (path = strchr(arg, ' '); path && (path[0] == ' '); ++path);
1314 if (!path || (path[0] == '\0')) {
1315 searchpaths = ly_ctx_get_searchdirs(ctx);
1316 if (searchpaths) {
1317 for (index = 0; searchpaths[index]; index++) {
1318 fprintf(stdout, "%s\n", searchpaths[index]);
1319 }
1320 }
1321 return 0;
1322 }
1323
1324 if ((!strncmp(path, "-h", 2) && (path[2] == '\0' || path[2] == ' ')) ||
1325 (!strncmp(path, "--help", 6) && (path[6] == '\0' || path[6] == ' '))) {
1326 cmd_searchpath_help();
1327 return 0;
1328 } else if (!strncmp(path, "--clear", 7) && (path[7] == '\0' || path[7] == ' ')) {
1329 ly_ctx_unset_searchdirs(ctx, NULL);
1330 return 0;
1331 }
1332
1333 if (stat(path, &st) == -1) {
1334 fprintf(stderr, "Failed to stat the search path (%s).\n", strerror(errno));
1335 return 1;
1336 }
1337 if (!S_ISDIR(st.st_mode)) {
1338 fprintf(stderr, "\"%s\" is not a directory.\n", path);
1339 return 1;
1340 }
1341
1342 ly_ctx_set_searchdir(ctx, path);
1343
1344 return 0;
1345}
1346
1347int
1348cmd_clear(const char *arg)
1349{
1350 struct ly_ctx *ctx_new;
1351 int options = 0;
1352#if 0
1353 int i;
1354 char *ylpath;
1355 const char * const *searchpaths;
1356 LYD_FORMAT format;
1357
1358 /* get optional yang library file name */
1359 for (i = 5; arg[i] && isspace(arg[i]); i++);
1360 if (arg[i]) {
1361 if (arg[i] == '-' && arg[i + 1] == 'e') {
1362 options = LY_CTX_NOYANGLIBRARY;
1363 goto create_empty;
1364 } else {
1365 ylpath = strdup(&arg[i]);
1366 format = detect_data_format(ylpath);
1367 if (format == LYD_UNKNOWN) {
1368 free(ylpath);
1369 fprintf(stderr, "Unable to resolve format of the yang library file, please add \".xml\" or \".json\" suffix.\n");
1370 goto create_empty;
1371 }
1372 searchpaths = ly_ctx_get_searchdirs(ctx);
1373 ctx_new = ly_ctx_new_ylpath(searchpaths ? searchpaths[0] : NULL, ylpath, format, 0);
1374 free(ylpath);
1375 }
1376 } else {
1377create_empty:
1378#else
Radek Krejci92f5ce92019-09-06 16:25:43 +02001379 (void) arg; /* TODO yang-library support */
Radek Krejcied5acc52019-04-25 15:57:04 +02001380 {
1381#endif
1382 ly_ctx_new(NULL, options, &ctx_new);
1383 }
1384
1385 if (!ctx_new) {
1386 fprintf(stderr, "Failed to create context.\n");
1387 return 1;
1388 }
1389
1390 /* final switch */
1391 ly_ctx_destroy(ctx, NULL);
1392 ctx = ctx_new;
1393
1394 return 0;
1395}
1396
1397int
1398cmd_verb(const char *arg)
1399{
1400 const char *verb;
1401 if (strlen(arg) < 5) {
1402 cmd_verb_help();
1403 return 1;
1404 }
1405
1406 verb = arg + 5;
1407 if (!strcmp(verb, "error") || !strcmp(verb, "0")) {
1408 ly_verb(LY_LLERR);
1409#ifndef NDEBUG
1410 ly_verb_dbg(0);
1411#endif
1412 } else if (!strcmp(verb, "warning") || !strcmp(verb, "1")) {
1413 ly_verb(LY_LLWRN);
1414#ifndef NDEBUG
1415 ly_verb_dbg(0);
1416#endif
1417 } else if (!strcmp(verb, "verbose") || !strcmp(verb, "2")) {
1418 ly_verb(LY_LLVRB);
1419#ifndef NDEBUG
1420 ly_verb_dbg(0);
1421#endif
1422 } else if (!strcmp(verb, "debug") || !strcmp(verb, "3")) {
1423 ly_verb(LY_LLDBG);
1424#ifndef NDEBUG
1425 ly_verb_dbg(LY_LDGDICT | LY_LDGYANG | LY_LDGYIN | LY_LDGXPATH | LY_LDGDIFF);
1426#endif
1427 } else {
1428 fprintf(stderr, "Unknown verbosity \"%s\"\n", verb);
1429 return 1;
1430 }
1431
1432 return 0;
1433}
1434
1435#ifndef NDEBUG
1436
1437int
1438cmd_debug(const char *arg)
1439{
1440 const char *beg, *end;
1441 int grps = 0;
1442 if (strlen(arg) < 6) {
1443 cmd_debug_help();
1444 return 1;
1445 }
1446
1447 end = arg + 6;
1448 while (end[0]) {
1449 for (beg = end; isspace(beg[0]); ++beg);
1450 if (!beg[0]) {
1451 break;
1452 }
1453
1454 for (end = beg; (end[0] && !isspace(end[0])); ++end);
1455
1456 if (!strncmp(beg, "dict", end - beg)) {
1457 grps |= LY_LDGDICT;
1458 } else if (!strncmp(beg, "yang", end - beg)) {
1459 grps |= LY_LDGYANG;
1460 } else if (!strncmp(beg, "yin", end - beg)) {
1461 grps |= LY_LDGYIN;
1462 } else if (!strncmp(beg, "xpath", end - beg)) {
1463 grps |= LY_LDGXPATH;
1464 } else if (!strncmp(beg, "diff", end - beg)) {
1465 grps |= LY_LDGDIFF;
1466 } else {
1467 fprintf(stderr, "Unknown debug group \"%.*s\"\n", (int)(end - beg), beg);
1468 return 1;
1469 }
1470 }
1471 ly_verb_dbg(grps);
1472
1473 return 0;
1474}
1475
1476#endif
1477
1478int
1479cmd_quit(const char *UNUSED(arg))
1480{
1481 done = 1;
1482 return 0;
1483}
1484
1485int
1486cmd_help(const char *arg)
1487{
1488 int i;
1489 char *args = strdup(arg);
1490 char *cmd = NULL;
1491
1492 strtok(args, " ");
1493 if ((cmd = strtok(NULL, " ")) == NULL) {
1494
1495generic_help:
1496 fprintf(stdout, "Available commands:\n");
1497
1498 for (i = 0; commands[i].name; i++) {
1499 if (commands[i].helpstring != NULL) {
1500 fprintf(stdout, " %-15s %s\n", commands[i].name, commands[i].helpstring);
1501 }
1502 }
1503 } else {
1504 /* print specific help for the selected command */
1505
1506 /* get the command of the specified name */
1507 for (i = 0; commands[i].name; i++) {
1508 if (strcmp(cmd, commands[i].name) == 0) {
1509 break;
1510 }
1511 }
1512
1513 /* execute the command's help if any valid command specified */
1514 if (commands[i].name) {
1515 if (commands[i].help_func != NULL) {
1516 commands[i].help_func();
1517 } else {
1518 printf("%s\n", commands[i].helpstring);
1519 }
1520 } else {
1521 /* if unknown command specified, print the list of commands */
1522 printf("Unknown command \'%s\'\n", cmd);
1523 goto generic_help;
1524 }
1525 }
1526
1527 free(args);
1528 return 0;
1529}
1530
1531COMMAND commands[] = {
1532 {"help", cmd_help, NULL, "Display commands description"},
1533 {"add", cmd_add, cmd_add_help, "Add a new model from a specific file"},
1534 {"load", cmd_load, cmd_load_help, "Load a new model from the searchdirs"},
1535 {"print", cmd_print, cmd_print_help, "Print a model"},
Radek Krejcied5acc52019-04-25 15:57:04 +02001536 {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
Radek Krejcie7b95092019-05-15 11:03:07 +02001537#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001538 {"xpath", cmd_xpath, cmd_xpath_help, "Get data nodes satisfying an XPath expression"},
1539 {"list", cmd_list, cmd_list_help, "List all the loaded models"},
1540#endif
1541 {"feature", cmd_feature, cmd_feature_help, "Print/enable/disable all/specific features of models"},
1542 {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for models"},
1543 {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded models"},
1544 {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
1545#ifndef NDEBUG
1546 {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
1547#endif
1548 {"quit", cmd_quit, NULL, "Quit the program"},
1549 /* synonyms for previous commands */
1550 {"?", cmd_help, NULL, "Display commands description"},
1551 {"exit", cmd_quit, NULL, "Quit the program"},
1552 {NULL, NULL, NULL, NULL}
1553};