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