blob: 20217fb175deb41a3710a65fe2e6778d58379f1f [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");
Radek Krejci56a98472020-10-12 12:23:16 +020067 printf("\t \t--tree-path <data-path>\t(print only the specified subtree)\n");
Radek Krejcied5acc52019-04-25 15:57:04 +020068 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 Krejci56a98472020-10-12 12:23:16 +020071 printf("\tinfo-path:\t<data-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{
Radek Krejcie58f97f2020-08-18 11:45:08 +0200189 int path_len, ret = 1;
Radek Krejcied5acc52019-04-25 15:57:04 +0200190 char *path, *dir, *s, *arg_ptr;
Radek Krejcied5acc52019-04-25 15:57:04 +0200191 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] == ' ') {
Michal Vasko25d6ad02020-10-22 12:20:22 +0200203 ly_ctx_set_options(ctx, LY_CTX_ALL_IMPLEMENTED);
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
Radek Krejcied5acc52019-04-25 15:57:04 +0200219 while (path) {
Radek Krejci83126492020-08-15 15:40:04 +0200220 int unset_path = 1;
Radek Krejcied5acc52019-04-25 15:57:04 +0200221 format = get_schema_format(path);
222 if (format == LYS_IN_UNKNOWN) {
223 free(path);
224 goto cleanup;
225 }
226
Radek Krejci83126492020-08-15 15:40:04 +0200227 /* add temporarily also the path of the module itself */
Radek Krejcied5acc52019-04-25 15:57:04 +0200228 dir = strdup(path);
Radek Krejci83126492020-08-15 15:40:04 +0200229 if (ly_ctx_set_searchdir(ctx, dirname(dir)) == LY_EEXIST) {
230 unset_path = 0;
231 }
232 /* parse the file */
Michal Vasko3a41dff2020-07-15 14:30:28 +0200233 lys_parse_path(ctx, path, format, &model);
Radek Krejcie58f97f2020-08-18 11:45:08 +0200234 ly_ctx_unset_searchdir_last(ctx, unset_path);
Radek Krejcied5acc52019-04-25 15:57:04 +0200235 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);
Michal Vasko25d6ad02020-10-22 12:20:22 +0200269 ly_ctx_unset_options(ctx, LY_CTX_ALL_IMPLEMENTED);
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] == ' ') {
Michal Vasko25d6ad02020-10-22 12:20:22 +0200290 ly_ctx_set_options(ctx, LY_CTX_ALL_IMPLEMENTED);
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);
Michal Vasko25d6ad02020-10-22 12:20:22 +0200335 ly_ctx_unset_options(ctx, LY_CTX_ALL_IMPLEMENTED);
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 Krejci52f65552020-09-01 17:03:35 +0200437 output_opts |= LYS_PRINT_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 Krejci56a98472020-10-12 12:23:16 +0200506 const struct lysc_node *node = ly_ctx_get_node(ctx, NULL, target_path, 0);
Radek Krejcia5bba312020-01-09 15:41:20 +0100507 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 {
Michal Vasko7c8439f2020-08-05 13:25:19 +0200513 ret = lys_print_module(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 int
Michal Vaskof03ed032020-03-04 13:31:44 +0100526parse_data(char *filepath, int *options, const struct lyd_node *tree, const char *rpc_act_file,
Radek Krejcied5acc52019-04-25 15:57:04 +0200527 struct lyd_node **result)
528{
Radek Krejcied5acc52019-04-25 15:57:04 +0200529 struct lyd_node *data = NULL, *rpc_act = NULL;
530 int opts = *options;
Radek Krejci7931b192020-06-25 17:05:03 +0200531 struct ly_in *in;
Radek Krejcied5acc52019-04-25 15:57:04 +0200532
Radek Krejci7931b192020-06-25 17:05:03 +0200533 if (ly_in_new_filepath(filepath, 0, &in)) {
534 fprintf(stderr, "Unable to open input YANG data file \"%s\".", filepath);
Radek Krejcied5acc52019-04-25 15:57:04 +0200535 return EXIT_FAILURE;
536 }
537
Radek Krejcie7b95092019-05-15 11:03:07 +0200538#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200539 if ((opts & LYD_OPT_TYPEMASK) == LYD_OPT_TYPEMASK) {
540 /* automatically detect data type from the data top level */
541 if (informat != LYD_XML) {
542 fprintf(stderr, "Only XML data can be automatically explored.\n");
543 return EXIT_FAILURE;
544 }
545
546 xml = lyxml_parse_path(ctx, filepath, 0);
547 if (!xml) {
548 fprintf(stderr, "Failed to parse XML data for automatic type detection.\n");
549 return EXIT_FAILURE;
550 }
551
552 /* NOTE: namespace is ignored to simplify usage of this feature */
553
554 if (!strcmp(xml->name, "data")) {
555 fprintf(stdout, "Parsing %s as complete datastore.\n", filepath);
556 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_ADD_YANGLIB;
557 } else if (!strcmp(xml->name, "config")) {
558 fprintf(stdout, "Parsing %s as config data.\n", filepath);
559 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
560 } else if (!strcmp(xml->name, "get-reply")) {
561 fprintf(stdout, "Parsing %s as <get> reply data.\n", filepath);
562 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
563 } else if (!strcmp(xml->name, "get-config-reply")) {
564 fprintf(stdout, "Parsing %s as <get-config> reply data.\n", filepath);
565 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
566 } else if (!strcmp(xml->name, "edit-config")) {
567 fprintf(stdout, "Parsing %s as <edit-config> data.\n", filepath);
568 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
569 } else if (!strcmp(xml->name, "rpc")) {
570 fprintf(stdout, "Parsing %s as <rpc> data.\n", filepath);
571 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
572 } else if (!strcmp(xml->name, "rpc-reply")) {
573 if (!rpc_act_file) {
574 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
575 lyxml_free(ctx, xml);
576 return EXIT_FAILURE;
577 }
578 fprintf(stdout, "Parsing %s as <rpc-reply> data.\n", filepath);
579 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
580 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, val_tree);
581 if (!rpc_act) {
582 fprintf(stderr, "Failed to parse RPC/action.\n");
583 lyxml_free(ctx, xml);
584 return EXIT_FAILURE;
585 }
586 } else if (!strcmp(xml->name, "notification")) {
587 fprintf(stdout, "Parsing %s as <notification> data.\n", filepath);
588 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
589 } else if (!strcmp(xml->name, "yang-data")) {
590 fprintf(stdout, "Parsing %s as <yang-data> data.\n", filepath);
591 opts = (opts & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
592 if (!rpc_act_file) {
593 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
594 lyxml_free(ctx, xml);
595 return EXIT_FAILURE;
596 }
597 } else {
598 fprintf(stderr, "Invalid top-level element for automatic data type recognition.\n");
599 lyxml_free(ctx, xml);
600 return EXIT_FAILURE;
601 }
602
603 if (opts & LYD_OPT_RPCREPLY) {
604 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act, val_tree);
605 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
606 data = lyd_parse_xml(ctx, &xml->child, opts, val_tree);
607 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
608 data = lyd_parse_xml(ctx, &xml->child, opts, rpc_act_file);
609 } else {
610 data = lyd_parse_xml(ctx, &xml->child, opts);
611 }
612 lyxml_free(ctx, xml);
613 } else {
614 if (opts & LYD_OPT_RPCREPLY) {
615 if (!rpc_act_file) {
616 fprintf(stderr, "RPC/action reply data require additional argument (file with the RPC/action).\n");
617 return EXIT_FAILURE;
618 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200619 rpc_act = lyd_parse_path(ctx, rpc_act_file, informat, LYD_OPT_RPC, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200620 if (!rpc_act) {
621 fprintf(stderr, "Failed to parse RPC/action.\n");
622 return EXIT_FAILURE;
623 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200624 if (trees) {
625 const struct lyd_node **trees_new;
626 unsigned int u;
627 trees_new = lyd_trees_new(1, rpc_act);
628
629 LY_ARRAY_FOR(trees, u) {
630 trees_new = lyd_trees_add(trees_new, trees[u]);
631 }
632 lyd_trees_free(trees, 0);
633 trees = trees_new;
634 } else {
635 trees = lyd_trees_new(1, rpc_act);
636 }
637 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200638 } else if (opts & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200639 data = lyd_parse_path(ctx, filepath, informat, opts, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200640 } else if (opts & LYD_OPT_DATA_TEMPLATE) {
641 if (!rpc_act_file) {
642 fprintf(stderr, "YANG-DATA require additional argument (name instance of yang-data extension).\n");
643 return EXIT_FAILURE;
644 }
645 data = lyd_parse_path(ctx, filepath, informat, opts, rpc_act_file);
646 } else {
Michal Vaskoa3881362020-01-21 15:57:35 +0100647#endif
Radek Krejci7931b192020-06-25 17:05:03 +0200648
Radek Krejci5536d282020-08-04 23:27:44 +0200649 lyd_parse_data(ctx, in, 0, opts, LYD_VALIDATE_PRESENT, &data);
Radek Krejcie7b95092019-05-15 11:03:07 +0200650#if 0
Michal Vaskoa3881362020-01-21 15:57:35 +0100651 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200652 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200653#endif
Radek Krejci7931b192020-06-25 17:05:03 +0200654 ly_in_free(in, 0);
655
Radek Krejcie7b95092019-05-15 11:03:07 +0200656 lyd_free_all(rpc_act);
Radek Krejcied5acc52019-04-25 15:57:04 +0200657
Radek Krejcie7b95092019-05-15 11:03:07 +0200658 if (ly_err_first(ctx)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200659 fprintf(stderr, "Failed to parse data.\n");
Radek Krejcie7b95092019-05-15 11:03:07 +0200660 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200661 return EXIT_FAILURE;
662 }
663
664 *result = data;
665 *options = opts;
666 return EXIT_SUCCESS;
667}
668
669int
670cmd_data(const char *arg)
671{
672 int c, argc, option_index, ret = 1;
673 int options = 0, printopt = 0;
674 char **argv = NULL, *ptr;
675 const char *out_path = NULL;
Michal Vaskof03ed032020-03-04 13:31:44 +0100676 struct lyd_node *data = NULL;
677 struct lyd_node *tree = NULL;
Michal Vasko52927e22020-03-16 17:26:14 +0100678 LYD_FORMAT outformat = 0;
Radek Krejci241f6b52020-05-21 18:13:49 +0200679 struct ly_out *out = NULL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200680 static struct option long_options[] = {
681 {"defaults", required_argument, 0, 'd'},
682 {"help", no_argument, 0, 'h'},
683 {"format", required_argument, 0, 'f'},
684 {"option", required_argument, 0, 't'},
685 {"output", required_argument, 0, 'o'},
686 {"running", required_argument, 0, 'r'},
687 {"strict", no_argument, 0, 's'},
688 {NULL, 0, 0, 0}
689 };
690 void *rlcd;
691
692 argc = 1;
693 argv = malloc(2*sizeof *argv);
694 *argv = strdup(arg);
695 ptr = strtok(*argv, " ");
696 while ((ptr = strtok(NULL, " "))) {
697 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
698 if (!rlcd) {
699 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
700 goto cleanup;
701 }
702 argv = rlcd;
703 argv[argc++] = ptr;
704 }
705 argv[argc] = NULL;
706
707 optind = 0;
708 while (1) {
709 option_index = 0;
710 c = getopt_long(argc, argv, "d:hf:o:st:r:", long_options, &option_index);
711 if (c == -1) {
712 break;
713 }
714
715 switch (c) {
716 case 'd':
717 if (!strcmp(optarg, "all")) {
Radek Krejciff61c882020-09-03 13:04:18 +0200718 printopt = (printopt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL;
Radek Krejcied5acc52019-04-25 15:57:04 +0200719 } else if (!strcmp(optarg, "all-tagged")) {
Radek Krejciff61c882020-09-03 13:04:18 +0200720 printopt = (printopt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_ALL_TAG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200721 } else if (!strcmp(optarg, "trim")) {
Radek Krejciff61c882020-09-03 13:04:18 +0200722 printopt = (printopt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_TRIM;
Radek Krejcied5acc52019-04-25 15:57:04 +0200723 } else if (!strcmp(optarg, "implicit-tagged")) {
Radek Krejciff61c882020-09-03 13:04:18 +0200724 printopt = (printopt & ~LYD_PRINT_WD_MASK) | LYD_PRINT_WD_IMPL_TAG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200725 }
726 break;
727 case 'h':
728 cmd_data_help();
729 ret = 0;
730 goto cleanup;
731 case 'f':
732 if (!strcmp(optarg, "xml")) {
733 outformat = LYD_XML;
734 } else if (!strcmp(optarg, "json")) {
735 outformat = LYD_JSON;
736 } else if (!strcmp(optarg, "lyb")) {
737 outformat = LYD_LYB;
738 } else {
739 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
740 goto cleanup;
741 }
742 break;
743 case 'o':
744 if (out_path) {
745 fprintf(stderr, "Output specified twice.\n");
746 goto cleanup;
747 }
748 out_path = optarg;
749 break;
Michal Vaskoa3881362020-01-21 15:57:35 +0100750#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200751 case 'r':
Radek Krejcied5acc52019-04-25 15:57:04 +0200752 if (optarg[0] == '!') {
753 /* ignore extenral dependencies to the running datastore */
754 options |= LYD_OPT_NOEXTDEPS;
755 } else {
756 /* external file with the running datastore */
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200757 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_DATA_NO_YANGLIB, trees);
Radek Krejcied5acc52019-04-25 15:57:04 +0200758 if (!val_tree) {
759 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
760 goto cleanup;
761 }
Radek Krejcif3b6fec2019-07-24 15:53:11 +0200762 if (!trees) {
763 trees = lyd_trees_new(1, val_tree);
764 } else {
765 trees = lyd_trees_add(trees, val_tree);
766 }
Radek Krejcied5acc52019-04-25 15:57:04 +0200767 }
768 break;
Michal Vaskoa3881362020-01-21 15:57:35 +0100769#endif
Radek Krejcidf5a0fe2020-09-03 13:46:48 +0200770 case 's':
771 options |= LYD_PARSE_STRICT;
772 break;
Radek Krejcied5acc52019-04-25 15:57:04 +0200773 case 't':
774 if (!strcmp(optarg, "auto")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100775 /* no flags */
Radek Krejcied5acc52019-04-25 15:57:04 +0200776 } else if (!strcmp(optarg, "data")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100777 /* no flags */
Michal Vaskob36053d2020-03-26 15:49:30 +0100778 /*} else if (!strcmp(optarg, "config")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100779 options |= LYD_OPT_CONFIG;
Radek Krejcied5acc52019-04-25 15:57:04 +0200780 } else if (!strcmp(optarg, "get")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100781 options |= LYD_OPT_GET;
Radek Krejcied5acc52019-04-25 15:57:04 +0200782 } else if (!strcmp(optarg, "getconfig")) {
Michal Vaskoa3881362020-01-21 15:57:35 +0100783 options |= LYD_OPT_GETCONFIG;
Michal Vaskob36053d2020-03-26 15:49:30 +0100784 } else if (!strcmp(optarg, "edit")) {
Michal Vasko9f96a052020-03-10 09:41:45 +0100785 options |= LYD_OPT_EDIT;*/
Radek Krejcied5acc52019-04-25 15:57:04 +0200786 } else {
787 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
788 cmd_data_help();
789 goto cleanup;
790 }
791 break;
792 case '?':
793 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
794 goto cleanup;
795 }
796 }
797
798 /* file name */
799 if (optind == argc) {
800 fprintf(stderr, "Missing the data file name.\n");
801 goto cleanup;
802 }
803
Michal Vaskof03ed032020-03-04 13:31:44 +0100804 if (parse_data(argv[optind], &options, tree, argv[optind + 1], &data)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200805 goto cleanup;
806 }
807
808 if (out_path) {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200809 ret = ly_out_new_filepath(out_path, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100810 } else {
Radek Krejci84ce7b12020-06-11 17:28:25 +0200811 ret = ly_out_new_file(stdout, &out);
Radek Krejcia5bba312020-01-09 15:41:20 +0100812 }
Radek Krejci84ce7b12020-06-11 17:28:25 +0200813 if (ret) {
Radek Krejcia5bba312020-01-09 15:41:20 +0100814 fprintf(stderr, "Could not open the output file (%s).\n", strerror(errno));
815 goto cleanup;
Radek Krejcied5acc52019-04-25 15:57:04 +0200816 }
817
Michal Vasko52927e22020-03-16 17:26:14 +0100818 if (outformat) {
Radek Krejci52f65552020-09-01 17:03:35 +0200819 ret = lyd_print_all(out, data, outformat, printopt);
Radek Krejci84ce7b12020-06-11 17:28:25 +0200820 ret = ret < 0 ? ret * (-1) : 0;
Radek Krejcied5acc52019-04-25 15:57:04 +0200821 }
822
Radek Krejcied5acc52019-04-25 15:57:04 +0200823cleanup:
824 free(*argv);
825 free(argv);
826
Radek Krejci241f6b52020-05-21 18:13:49 +0200827 ly_out_free(out, NULL, out_path ? 1 : 0);
Radek Krejcied5acc52019-04-25 15:57:04 +0200828
Radek Krejcie7b95092019-05-15 11:03:07 +0200829 lyd_free_all(data);
Radek Krejcied5acc52019-04-25 15:57:04 +0200830
831 return ret;
832}
Radek Krejcie7b95092019-05-15 11:03:07 +0200833#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +0200834int
835cmd_xpath(const char *arg)
836{
837 int c, argc, option_index, ret = 1, long_str;
838 char **argv = NULL, *ptr, *expr = NULL;
839 unsigned int i, j;
840 int options = 0;
841 struct lyd_node *data = NULL, *node, *val_tree = NULL;
842 struct lyd_node_leaf_list *key;
843 struct ly_set *set;
844 static struct option long_options[] = {
845 {"help", no_argument, 0, 'h'},
846 {"expr", required_argument, 0, 'e'},
847 {NULL, 0, 0, 0}
848 };
849 void *rlcd;
850
851 long_str = 0;
852 argc = 1;
853 argv = malloc(2 * sizeof *argv);
854 *argv = strdup(arg);
855 ptr = strtok(*argv, " ");
856 while ((ptr = strtok(NULL, " "))) {
857 if (long_str) {
858 ptr[-1] = ' ';
859 if (ptr[strlen(ptr) - 1] == long_str) {
860 long_str = 0;
861 ptr[strlen(ptr) - 1] = '\0';
862 }
863 } else {
864 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
865 if (!rlcd) {
866 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
867 goto cleanup;
868 }
869 argv = rlcd;
870 argv[argc] = ptr;
871 if (ptr[0] == '"') {
872 long_str = '"';
873 ++argv[argc];
874 }
875 if (ptr[0] == '\'') {
876 long_str = '\'';
877 ++argv[argc];
878 }
879 if (ptr[strlen(ptr) - 1] == long_str) {
880 long_str = 0;
881 ptr[strlen(ptr) - 1] = '\0';
882 }
883 ++argc;
884 }
885 }
886 argv[argc] = NULL;
887
888 optind = 0;
889 while (1) {
890 option_index = 0;
891 c = getopt_long(argc, argv, "he:t:x:", long_options, &option_index);
892 if (c == -1) {
893 break;
894 }
895
896 switch (c) {
897 case 'h':
898 cmd_xpath_help();
899 ret = 0;
900 goto cleanup;
901 case 'e':
902 expr = optarg;
903 break;
904 case 't':
905 if (!strcmp(optarg, "auto")) {
906 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_TYPEMASK;
907 } else if (!strcmp(optarg, "config")) {
908 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_CONFIG;
909 } else if (!strcmp(optarg, "get")) {
910 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GET;
911 } else if (!strcmp(optarg, "getconfig")) {
912 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_GETCONFIG;
913 } else if (!strcmp(optarg, "edit")) {
914 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_EDIT;
915 } else if (!strcmp(optarg, "rpc")) {
916 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPC;
917 } else if (!strcmp(optarg, "rpcreply")) {
918 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_RPCREPLY;
919 } else if (!strcmp(optarg, "notif")) {
920 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_NOTIF;
921 } else if (!strcmp(optarg, "yangdata")) {
922 options = (options & ~LYD_OPT_TYPEMASK) | LYD_OPT_DATA_TEMPLATE;
923 } else {
924 fprintf(stderr, "Invalid parser option \"%s\".\n", optarg);
925 cmd_data_help();
926 goto cleanup;
927 }
928 break;
929 case 'x':
930 val_tree = lyd_parse_path(ctx, optarg, LYD_XML, LYD_OPT_CONFIG);
931 if (!val_tree) {
932 fprintf(stderr, "Failed to parse the additional data tree for validation.\n");
933 goto cleanup;
934 }
935 break;
936 case '?':
937 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
938 goto cleanup;
939 }
940 }
941
942 if (optind == argc) {
943 fprintf(stderr, "Missing the file with data.\n");
944 goto cleanup;
945 }
946
947 if (!expr) {
948 fprintf(stderr, "Missing the XPath expression.\n");
949 goto cleanup;
950 }
951
952 if (parse_data(argv[optind], &options, val_tree, argv[optind + 1], &data)) {
953 goto cleanup;
954 }
955
956 if (!(set = lyd_find_path(data, expr))) {
957 goto cleanup;
958 }
959
960 /* print result */
961 printf("Result:\n");
962 if (!set->number) {
963 printf("\tEmpty\n");
964 } else {
965 for (i = 0; i < set->number; ++i) {
966 node = set->set.d[i];
967 switch (node->schema->nodetype) {
968 case LYS_CONTAINER:
969 printf("\tContainer ");
970 break;
971 case LYS_LEAF:
972 printf("\tLeaf ");
973 break;
974 case LYS_LEAFLIST:
975 printf("\tLeaflist ");
976 break;
977 case LYS_LIST:
978 printf("\tList ");
979 break;
980 case LYS_ANYXML:
981 printf("\tAnyxml ");
982 break;
983 case LYS_ANYDATA:
984 printf("\tAnydata ");
985 break;
986 default:
987 printf("\tUnknown ");
988 break;
989 }
990 printf("\"%s\"", node->schema->name);
991 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
992 printf(" (val: %s)", ((struct lyd_node_leaf_list *)node)->value_str);
993 } else if (node->schema->nodetype == LYS_LIST) {
994 key = (struct lyd_node_leaf_list *)node->child;
995 printf(" (");
996 for (j = 0; j < ((struct lys_node_list *)node->schema)->keys_size; ++j) {
997 if (j) {
998 printf(" ");
999 }
1000 printf("\"%s\": %s", key->schema->name, key->value_str);
1001 key = (struct lyd_node_leaf_list *)key->next;
1002 }
1003 printf(")");
1004 }
1005 printf("\n");
1006 }
1007 }
1008 printf("\n");
1009
1010 ly_set_free(set);
1011 ret = 0;
1012
1013cleanup:
1014 free(*argv);
1015 free(argv);
1016
1017 lyd_free_withsiblings(data);
1018
1019 return ret;
1020}
Radek Krejci2a7a39e2020-08-21 10:39:17 +02001021#endif
Radek Krejcied5acc52019-04-25 15:57:04 +02001022
1023int
1024print_list(FILE *out, struct ly_ctx *ctx, LYD_FORMAT outformat)
1025{
1026 struct lyd_node *ylib;
1027 uint32_t idx = 0, has_modules = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +02001028 const struct lys_module *mod;
1029
1030 if (outformat != LYD_UNKNOWN) {
Radek Krejci2a7a39e2020-08-21 10:39:17 +02001031 if (ly_ctx_get_yanglib_data(ctx, &ylib)) {
Radek Krejcied5acc52019-04-25 15:57:04 +02001032 fprintf(stderr, "Getting context info (ietf-yang-library data) failed.\n");
1033 return 1;
1034 }
1035
Radek Krejci52f65552020-09-01 17:03:35 +02001036 lyd_print_file(out, ylib, outformat, LYD_PRINT_WITHSIBLINGS);
Radek Krejci2a7a39e2020-08-21 10:39:17 +02001037 lyd_free_all(ylib);
Radek Krejcied5acc52019-04-25 15:57:04 +02001038 return 0;
1039 }
1040
1041 /* iterate schemas in context and provide just the basic info */
1042 fprintf(out, "List of the loaded models:\n");
1043 while ((mod = ly_ctx_get_module_iter(ctx, &idx))) {
1044 has_modules++;
1045
1046 /* conformance print */
1047 if (mod->implemented) {
1048 fprintf(out, "\tI");
1049 } else {
1050 fprintf(out, "\ti");
1051 }
1052
1053 /* module print */
1054 fprintf(out, " %s", mod->name);
Radek Krejci2a7a39e2020-08-21 10:39:17 +02001055 if (mod->revision) {
1056 fprintf(out, "@%s", mod->revision);
Radek Krejcied5acc52019-04-25 15:57:04 +02001057 }
1058
1059 /* submodules print */
Radek Krejci2a7a39e2020-08-21 10:39:17 +02001060 if (mod->parsed && mod->parsed->includes) {
1061 uint64_t u = 0;
Radek Krejcied5acc52019-04-25 15:57:04 +02001062 fprintf(out, " (");
Radek Krejci2a7a39e2020-08-21 10:39:17 +02001063 LY_ARRAY_FOR(mod->parsed->includes, u) {
1064 fprintf(out, "%s%s", !u ? "" : ",", mod->parsed->includes[u].name);
1065 if (mod->parsed->includes[u].rev[0]) {
1066 fprintf(out, "@%s", mod->parsed->includes[u].rev);
Radek Krejcied5acc52019-04-25 15:57:04 +02001067 }
1068 }
1069 fprintf(out, ")");
1070 }
1071
1072 /* finish the line */
1073 fprintf(out, "\n");
1074 }
1075
1076 if (!has_modules) {
1077 fprintf(out, "\t(none)\n");
1078 }
1079
1080 return 0;
1081}
1082
1083int
1084cmd_list(const char *arg)
1085{
1086 char **argv = NULL, *ptr;
1087 int c, argc, option_index;
1088 LYD_FORMAT outformat = LYD_UNKNOWN;
1089 static struct option long_options[] = {
1090 {"help", no_argument, 0, 'h'},
1091 {"format", required_argument, 0, 'f'},
1092 {NULL, 0, 0, 0}
1093 };
1094 void *rlcd;
1095
1096 argc = 1;
1097 argv = malloc(2*sizeof *argv);
1098 *argv = strdup(arg);
1099 ptr = strtok(*argv, " ");
1100 while ((ptr = strtok(NULL, " "))) {
1101 rlcd = realloc(argv, (argc+2)*sizeof *argv);
1102 if (!rlcd) {
1103 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1104 goto error;
1105 }
1106 argv = rlcd;
1107 argv[argc++] = ptr;
1108 }
1109 argv[argc] = NULL;
1110
1111 optind = 0;
1112 while (1) {
1113 option_index = 0;
1114 c = getopt_long(argc, argv, "hf:", long_options, &option_index);
1115 if (c == -1) {
1116 break;
1117 }
1118
1119 switch (c) {
1120 case 'h':
1121 cmd_data_help();
1122 free(*argv);
1123 free(argv);
1124 return 0;
1125 case 'f':
1126 if (!strcmp(optarg, "xml")) {
1127 outformat = LYD_XML;
1128 } else if (!strcmp(optarg, "json")) {
1129 outformat = LYD_JSON;
1130 } else {
1131 fprintf(stderr, "Unknown output format \"%s\".\n", optarg);
1132 goto error;
1133 }
1134 break;
1135 case '?':
1136 /* getopt_long() prints message */
1137 goto error;
1138 }
1139 }
1140 if (optind != argc) {
1141 fprintf(stderr, "Unknown parameter \"%s\"\n", argv[optind]);
1142error:
1143 free(*argv);
1144 free(argv);
1145 return 1;
1146 }
1147 free(*argv);
1148 free(argv);
1149
1150 return print_list(stdout, ctx, outformat);
1151}
Radek Krejci2a7a39e2020-08-21 10:39:17 +02001152
Radek Krejcied5acc52019-04-25 15:57:04 +02001153int
1154cmd_feature(const char *arg)
1155{
1156 int c, argc, option_index, ret = 1, task = 0;
1157 char **argv = NULL, *ptr, *model_name, *revision, *feat_names = NULL;
1158 const struct lys_module *module;
1159 static struct option long_options[] = {
1160 {"help", no_argument, 0, 'h'},
1161 {"enable", required_argument, 0, 'e'},
1162 {"disable", required_argument, 0, 'd'},
1163 {NULL, 0, 0, 0}
1164 };
1165 void *rlcd;
1166
1167 argc = 1;
1168 argv = malloc(2*sizeof *argv);
1169 *argv = strdup(arg);
1170 ptr = strtok(*argv, " ");
1171 while ((ptr = strtok(NULL, " "))) {
1172 rlcd = realloc(argv, (argc + 2) * sizeof *argv);
1173 if (!rlcd) {
1174 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
1175 goto cleanup;
1176 }
1177 argv = rlcd;
1178 argv[argc++] = ptr;
1179 }
1180 argv[argc] = NULL;
1181
1182 optind = 0;
1183 while (1) {
1184 option_index = 0;
1185 c = getopt_long(argc, argv, "he:d:", long_options, &option_index);
1186 if (c == -1) {
1187 break;
1188 }
1189
1190 switch (c) {
1191 case 'h':
1192 cmd_feature_help();
1193 ret = 0;
1194 goto cleanup;
1195 case 'e':
1196 if (task) {
1197 fprintf(stderr, "Only one of enable or disable can be specified.\n");
1198 goto cleanup;
1199 }
1200 task = 1;
1201 feat_names = optarg;
1202 break;
1203 case 'd':
1204 if (task) {
1205 fprintf(stderr, "Only one of enable, or disable can be specified.\n");
1206 goto cleanup;
1207 }
1208 task = 2;
1209 feat_names = optarg;
1210 break;
1211 case '?':
1212 fprintf(stderr, "Unknown option \"%d\".\n", (char)c);
1213 goto cleanup;
1214 }
1215 }
1216
1217 /* module name */
1218 if (optind == argc) {
1219 fprintf(stderr, "Missing the module name.\n");
1220 goto cleanup;
1221 }
1222
1223 revision = NULL;
1224 model_name = argv[optind];
1225 if (strchr(model_name, '@')) {
1226 revision = strchr(model_name, '@');
1227 revision[0] = '\0';
1228 ++revision;
1229 }
1230
Radek Krejci3d46f612020-08-13 12:01:53 +02001231 if (!revision) {
1232 module = ly_ctx_get_module_implemented(ctx, model_name);
1233 } else {
1234 module = ly_ctx_get_module(ctx, model_name, revision);
1235 if (module && !module->implemented) {
1236 module = NULL;
1237 }
1238 }
Radek Krejcied5acc52019-04-25 15:57:04 +02001239#if 0
1240 if (!module) {
1241 /* not a module, try to find it as a submodule */
1242 module = (const struct lys_module *)ly_ctx_get_submodule(ctx, NULL, NULL, model_name, revision);
1243 }
1244#endif
1245
1246 if (module == NULL) {
1247 if (revision) {
Radek Krejci3d46f612020-08-13 12:01:53 +02001248 fprintf(stderr, "No implemented (sub)module \"%s\" in revision %s found.\n", model_name, revision);
Radek Krejcied5acc52019-04-25 15:57:04 +02001249 } else {
Radek Krejci3d46f612020-08-13 12:01:53 +02001250 fprintf(stderr, "No implemented (sub)module \"%s\" found.\n", model_name);
Radek Krejcied5acc52019-04-25 15:57:04 +02001251 }
1252 goto cleanup;
1253 }
1254
1255 if (!task) {
Radek Krejci7eb54ba2020-05-18 16:30:04 +02001256 size_t len, max_len = 0;
Michal Vaskofd69e1d2020-07-03 11:57:17 +02001257 LY_ARRAY_COUNT_TYPE u;
Radek Krejcied5acc52019-04-25 15:57:04 +02001258 struct lysc_feature *features;
1259
1260 printf("%s features:\n", module->name);
1261
Radek Krejci14915cc2020-09-14 17:28:13 +02001262 features = module->features;
Radek Krejcied5acc52019-04-25 15:57:04 +02001263
1264 /* get the max len */
1265 LY_ARRAY_FOR(features, u) {
1266 len = strlen(features[u].name);
1267 if (len > max_len) {
1268 max_len = len;
1269 }
1270 }
1271
1272 LY_ARRAY_FOR(features, u) {
Radek Krejci7eb54ba2020-05-18 16:30:04 +02001273 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 +02001274 }
1275 if (!u) {
1276 printf("\t(none)\n");
1277 }
1278 } else {
1279 feat_names = strtok(feat_names, ",");
1280 while (feat_names) {
1281 if (((task == 1) && lys_feature_enable(module, feat_names))
1282 || ((task == 2) && lys_feature_disable(module, feat_names))) {
1283 fprintf(stderr, "Feature \"%s\" not found.\n", feat_names);
1284 ret = 1;
1285 }
1286 feat_names = strtok(NULL, ",");
1287 }
1288 }
1289
1290cleanup:
1291 free(*argv);
1292 free(argv);
1293
1294 return ret;
1295}
1296
1297int
1298cmd_searchpath(const char *arg)
1299{
1300 const char *path;
1301 const char * const *searchpaths;
1302 int index;
1303 struct stat st;
1304
1305 for (path = strchr(arg, ' '); path && (path[0] == ' '); ++path);
1306 if (!path || (path[0] == '\0')) {
1307 searchpaths = ly_ctx_get_searchdirs(ctx);
1308 if (searchpaths) {
1309 for (index = 0; searchpaths[index]; index++) {
1310 fprintf(stdout, "%s\n", searchpaths[index]);
1311 }
1312 }
1313 return 0;
1314 }
1315
1316 if ((!strncmp(path, "-h", 2) && (path[2] == '\0' || path[2] == ' ')) ||
1317 (!strncmp(path, "--help", 6) && (path[6] == '\0' || path[6] == ' '))) {
1318 cmd_searchpath_help();
1319 return 0;
1320 } else if (!strncmp(path, "--clear", 7) && (path[7] == '\0' || path[7] == ' ')) {
Radek Krejcie58f97f2020-08-18 11:45:08 +02001321 ly_ctx_unset_searchdir(ctx, NULL);
Radek Krejcied5acc52019-04-25 15:57:04 +02001322 return 0;
1323 }
1324
1325 if (stat(path, &st) == -1) {
1326 fprintf(stderr, "Failed to stat the search path (%s).\n", strerror(errno));
1327 return 1;
1328 }
1329 if (!S_ISDIR(st.st_mode)) {
1330 fprintf(stderr, "\"%s\" is not a directory.\n", path);
1331 return 1;
1332 }
1333
1334 ly_ctx_set_searchdir(ctx, path);
1335
1336 return 0;
1337}
1338
1339int
1340cmd_clear(const char *arg)
1341{
1342 struct ly_ctx *ctx_new;
1343 int options = 0;
1344#if 0
1345 int i;
1346 char *ylpath;
1347 const char * const *searchpaths;
1348 LYD_FORMAT format;
1349
1350 /* get optional yang library file name */
1351 for (i = 5; arg[i] && isspace(arg[i]); i++);
1352 if (arg[i]) {
1353 if (arg[i] == '-' && arg[i + 1] == 'e') {
1354 options = LY_CTX_NOYANGLIBRARY;
1355 goto create_empty;
1356 } else {
1357 ylpath = strdup(&arg[i]);
1358 format = detect_data_format(ylpath);
1359 if (format == LYD_UNKNOWN) {
1360 free(ylpath);
1361 fprintf(stderr, "Unable to resolve format of the yang library file, please add \".xml\" or \".json\" suffix.\n");
1362 goto create_empty;
1363 }
1364 searchpaths = ly_ctx_get_searchdirs(ctx);
1365 ctx_new = ly_ctx_new_ylpath(searchpaths ? searchpaths[0] : NULL, ylpath, format, 0);
1366 free(ylpath);
1367 }
1368 } else {
1369create_empty:
1370#else
Radek Krejci92f5ce92019-09-06 16:25:43 +02001371 (void) arg; /* TODO yang-library support */
Radek Krejcied5acc52019-04-25 15:57:04 +02001372 {
1373#endif
1374 ly_ctx_new(NULL, options, &ctx_new);
1375 }
1376
1377 if (!ctx_new) {
1378 fprintf(stderr, "Failed to create context.\n");
1379 return 1;
1380 }
1381
1382 /* final switch */
1383 ly_ctx_destroy(ctx, NULL);
1384 ctx = ctx_new;
1385
1386 return 0;
1387}
1388
1389int
1390cmd_verb(const char *arg)
1391{
1392 const char *verb;
1393 if (strlen(arg) < 5) {
1394 cmd_verb_help();
1395 return 1;
1396 }
1397
1398 verb = arg + 5;
1399 if (!strcmp(verb, "error") || !strcmp(verb, "0")) {
Radek Krejci52b6d512020-10-12 12:33:17 +02001400 ly_log_level(LY_LLERR);
Radek Krejcied5acc52019-04-25 15:57:04 +02001401#ifndef NDEBUG
Radek Krejci68433c92020-10-12 17:03:55 +02001402 ly_log_dbg_groups(0);
Radek Krejcied5acc52019-04-25 15:57:04 +02001403#endif
1404 } else if (!strcmp(verb, "warning") || !strcmp(verb, "1")) {
Radek Krejci52b6d512020-10-12 12:33:17 +02001405 ly_log_level(LY_LLWRN);
Radek Krejcied5acc52019-04-25 15:57:04 +02001406#ifndef NDEBUG
Radek Krejci68433c92020-10-12 17:03:55 +02001407 ly_log_dbg_groups(0);
Radek Krejcied5acc52019-04-25 15:57:04 +02001408#endif
1409 } else if (!strcmp(verb, "verbose") || !strcmp(verb, "2")) {
Radek Krejci52b6d512020-10-12 12:33:17 +02001410 ly_log_level(LY_LLVRB);
Radek Krejcied5acc52019-04-25 15:57:04 +02001411#ifndef NDEBUG
Radek Krejci68433c92020-10-12 17:03:55 +02001412 ly_log_dbg_groups(0);
Radek Krejcied5acc52019-04-25 15:57:04 +02001413#endif
1414 } else if (!strcmp(verb, "debug") || !strcmp(verb, "3")) {
Radek Krejci52b6d512020-10-12 12:33:17 +02001415 ly_log_level(LY_LLDBG);
Radek Krejcied5acc52019-04-25 15:57:04 +02001416#ifndef NDEBUG
Radek Krejci68433c92020-10-12 17:03:55 +02001417 ly_log_dbg_groups(LY_LDGDICT | LY_LDGYANG | LY_LDGYIN | LY_LDGXPATH | LY_LDGDIFF);
Radek Krejcied5acc52019-04-25 15:57:04 +02001418#endif
1419 } else {
1420 fprintf(stderr, "Unknown verbosity \"%s\"\n", verb);
1421 return 1;
1422 }
1423
1424 return 0;
1425}
1426
1427#ifndef NDEBUG
1428
1429int
1430cmd_debug(const char *arg)
1431{
1432 const char *beg, *end;
1433 int grps = 0;
1434 if (strlen(arg) < 6) {
1435 cmd_debug_help();
1436 return 1;
1437 }
1438
1439 end = arg + 6;
1440 while (end[0]) {
1441 for (beg = end; isspace(beg[0]); ++beg);
1442 if (!beg[0]) {
1443 break;
1444 }
1445
1446 for (end = beg; (end[0] && !isspace(end[0])); ++end);
1447
1448 if (!strncmp(beg, "dict", end - beg)) {
1449 grps |= LY_LDGDICT;
1450 } else if (!strncmp(beg, "yang", end - beg)) {
1451 grps |= LY_LDGYANG;
1452 } else if (!strncmp(beg, "yin", end - beg)) {
1453 grps |= LY_LDGYIN;
1454 } else if (!strncmp(beg, "xpath", end - beg)) {
1455 grps |= LY_LDGXPATH;
1456 } else if (!strncmp(beg, "diff", end - beg)) {
1457 grps |= LY_LDGDIFF;
1458 } else {
1459 fprintf(stderr, "Unknown debug group \"%.*s\"\n", (int)(end - beg), beg);
1460 return 1;
1461 }
1462 }
Radek Krejci68433c92020-10-12 17:03:55 +02001463 ly_log_dbg_groups(grps);
Radek Krejcied5acc52019-04-25 15:57:04 +02001464
1465 return 0;
1466}
1467
1468#endif
1469
1470int
1471cmd_quit(const char *UNUSED(arg))
1472{
1473 done = 1;
1474 return 0;
1475}
1476
1477int
1478cmd_help(const char *arg)
1479{
1480 int i;
1481 char *args = strdup(arg);
1482 char *cmd = NULL;
1483
1484 strtok(args, " ");
1485 if ((cmd = strtok(NULL, " ")) == NULL) {
1486
1487generic_help:
1488 fprintf(stdout, "Available commands:\n");
1489
1490 for (i = 0; commands[i].name; i++) {
1491 if (commands[i].helpstring != NULL) {
1492 fprintf(stdout, " %-15s %s\n", commands[i].name, commands[i].helpstring);
1493 }
1494 }
1495 } else {
1496 /* print specific help for the selected command */
1497
1498 /* get the command of the specified name */
1499 for (i = 0; commands[i].name; i++) {
1500 if (strcmp(cmd, commands[i].name) == 0) {
1501 break;
1502 }
1503 }
1504
1505 /* execute the command's help if any valid command specified */
1506 if (commands[i].name) {
1507 if (commands[i].help_func != NULL) {
1508 commands[i].help_func();
1509 } else {
1510 printf("%s\n", commands[i].helpstring);
1511 }
1512 } else {
1513 /* if unknown command specified, print the list of commands */
1514 printf("Unknown command \'%s\'\n", cmd);
1515 goto generic_help;
1516 }
1517 }
1518
1519 free(args);
1520 return 0;
1521}
1522
1523COMMAND commands[] = {
1524 {"help", cmd_help, NULL, "Display commands description"},
1525 {"add", cmd_add, cmd_add_help, "Add a new model from a specific file"},
1526 {"load", cmd_load, cmd_load_help, "Load a new model from the searchdirs"},
1527 {"print", cmd_print, cmd_print_help, "Print a model"},
Radek Krejcied5acc52019-04-25 15:57:04 +02001528 {"data", cmd_data, cmd_data_help, "Load, validate and optionally print instance data"},
Radek Krejcie7b95092019-05-15 11:03:07 +02001529#if 0
Radek Krejcied5acc52019-04-25 15:57:04 +02001530 {"xpath", cmd_xpath, cmd_xpath_help, "Get data nodes satisfying an XPath expression"},
Radek Krejcied5acc52019-04-25 15:57:04 +02001531#endif
Radek Krejci2a7a39e2020-08-21 10:39:17 +02001532 {"list", cmd_list, cmd_list_help, "List all the loaded models"},
Radek Krejcied5acc52019-04-25 15:57:04 +02001533 {"feature", cmd_feature, cmd_feature_help, "Print/enable/disable all/specific features of models"},
1534 {"searchpath", cmd_searchpath, cmd_searchpath_help, "Print/set the search path(s) for models"},
1535 {"clear", cmd_clear, cmd_clear_help, "Clear the context - remove all the loaded models"},
1536 {"verb", cmd_verb, cmd_verb_help, "Change verbosity"},
1537#ifndef NDEBUG
1538 {"debug", cmd_debug, cmd_debug_help, "Display specific debug message groups"},
1539#endif
1540 {"quit", cmd_quit, NULL, "Quit the program"},
1541 /* synonyms for previous commands */
1542 {"?", cmd_help, NULL, "Display commands description"},
1543 {"exit", cmd_quit, NULL, "Quit the program"},
1544 {NULL, NULL, NULL, NULL}
1545};