blob: 0017044f34c4ac45ab500dd12ba3d3e2835a0bc6 [file] [log] [blame]
Radek Krejcie9f13b12020-11-09 17:42:04 +01001/**
2 * @file cmd_print.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @author Radek Krejci <rkrejci@cesnet.cz>
5 * @brief 'print' command of the libyang's yanglint tool.
6 *
7 * Copyright (c) 2015-2020 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
16#define _GNU_SOURCE
17
18#include "cmd.h"
19
20#include <errno.h>
21#include <getopt.h>
22#include <stdint.h>
23#include <stdio.h>
24#include <string.h>
25#include <strings.h>
26
27#include "libyang.h"
28
29#include "common.h"
30
31void
32cmd_print_help(void)
33{
aPiecek2cfeeae2021-04-21 13:17:34 +020034 printf("Usage: print [-f (yang | yin | tree [-q -P PATH -L LINE_LENGTH ] | info [-q -P PATH])]\n"
35 " [-o OUTFILE] [<module-name1>[@revision]] ...\n"
Radek Krejcie9f13b12020-11-09 17:42:04 +010036 " Print a schema module. The <module-name> is not required\n"
aPiecekdbe35d22023-03-31 10:33:15 +020037 " only in case the -P option is specified. For yang, yin and tree\n"
38 " formats, a submodule can also be printed.\n\n"
Radek Krejcie9f13b12020-11-09 17:42:04 +010039 " -f FORMAT, --format=FORMAT\n"
40 " Print the module in the specified FORMAT. If format not\n"
41 " specified, the 'tree' format is used.\n"
aPiecek2cfeeae2021-04-21 13:17:34 +020042 " -L LINE_LENGTH, --tree-line-length=LINE_LENGTH\n"
43 " The limit of the maximum line length on which the 'tree'\n"
44 " format will try to be printed.\n"
Radek Krejcie9f13b12020-11-09 17:42:04 +010045 " -P PATH, --schema-node=PATH\n"
46 " Print only the specified subtree of the schema.\n"
47 " The PATH is the XPath subset mentioned in documentation as\n"
48 " the Path format. The option can be combined with --single-node\n"
49 " option to print information only about the specified node.\n"
50 " -q, --single-node\n"
51 " Supplement to the --schema-node option to print information\n"
52 " only about a single node specified as PATH argument.\n"
53 " -o OUTFILE, --output=OUTFILE\n"
54 " Write the output to OUTFILE instead of stdout.\n");
55}
56
aPiecek0bea9872021-04-21 11:13:20 +020057static LY_ERR
58cmd_print_submodule(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
59{
60 LY_ERR erc;
61 const struct lysp_submodule *submodule;
62
63 submodule = revision ?
64 ly_ctx_get_submodule(*ctx, name, revision) :
65 ly_ctx_get_submodule_latest(*ctx, name);
66
67 erc = submodule ?
68 lys_print_submodule(out, submodule, format, line_length, options) :
69 LY_ENOTFOUND;
70
71 return erc;
72}
73
74static LY_ERR
75cmd_print_module(struct ly_out *out, struct ly_ctx **ctx, char *name, char *revision, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
76{
77 LY_ERR erc;
78 struct lys_module *module;
79
80 module = revision ?
81 ly_ctx_get_module(*ctx, name, revision) :
82 ly_ctx_get_module_latest(*ctx, name);
83
84 erc = module ?
85 lys_print_module(out, module, format, line_length, options) :
86 LY_ENOTFOUND;
87
88 return erc;
89}
90
91static void
92cmd_print_modules(int argc, char **argv, struct ly_out *out, struct ly_ctx **ctx, LYS_OUTFORMAT format, size_t line_length, uint32_t options)
93{
94 LY_ERR erc;
95 char *name, *revision;
96 ly_bool search_submodul;
aPiecek8f0b7882021-05-21 10:18:36 +020097 const int stop = argc - optind;
aPiecek0bea9872021-04-21 11:13:20 +020098
aPiecek8f0b7882021-05-21 10:18:36 +020099 for (int i = 0; i < stop; i++) {
aPiecek0bea9872021-04-21 11:13:20 +0200100 name = argv[optind + i];
101 /* get revision */
102 revision = strchr(name, '@');
103 if (revision) {
104 revision[0] = '\0';
105 ++revision;
106 }
107
108 erc = cmd_print_module(out, ctx, name, revision, format, line_length, options);
109
110 if (erc == LY_ENOTFOUND) {
111 search_submodul = 1;
112 erc = cmd_print_submodule(out, ctx, name, revision, format, line_length, options);
113 } else {
114 search_submodul = 0;
115 }
116
117 if (erc == LY_SUCCESS) {
aPiecek8f0b7882021-05-21 10:18:36 +0200118 /* for YANG Tree Diagrams printing it's more readable to print a blank line between modules. */
119 if ((format == LYS_OUT_TREE) && (i + 1 < stop)) {
120 ly_print(out, "\n");
121 }
aPiecek0bea9872021-04-21 11:13:20 +0200122 continue;
123 } else if (erc == LY_ENOTFOUND) {
124 if (revision) {
125 YLMSG_E("No (sub)module \"%s\" in revision %s found.\n", name, revision);
126 } else {
127 YLMSG_E("No (sub)module \"%s\" found.\n", name);
128 }
129 break;
130 } else {
131 if (search_submodul) {
132 YLMSG_E("Unable to print submodule %s.\n", name);
133 } else {
134 YLMSG_E("Unable to print module %s.\n", name);
135 }
136 break;
137 }
138 }
139}
140
Radek Krejcie9f13b12020-11-09 17:42:04 +0100141void
142cmd_print(struct ly_ctx **ctx, const char *cmdline)
143{
Radek Krejcie9f13b12020-11-09 17:42:04 +0100144 int argc = 0;
145 char **argv = NULL;
146 int opt, opt_index;
147 struct option options[] = {
148 {"format", required_argument, NULL, 'f'},
149 {"help", no_argument, NULL, 'h'},
aPiecek2cfeeae2021-04-21 13:17:34 +0200150 {"tree-line-length", required_argument, NULL, 'L'},
Radek Krejcie9f13b12020-11-09 17:42:04 +0100151 {"output", required_argument, NULL, 'o'},
152 {"schema-node", required_argument, NULL, 'P'},
153 {"single-node", no_argument, NULL, 'q'},
154 {NULL, 0, NULL, 0}
155 };
156 uint16_t options_print = 0;
157 const char *node_path = NULL;
158 LYS_OUTFORMAT format = LYS_OUT_TREE;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100159 struct ly_out *out = NULL;
Radek Krejci4b0f01b2021-03-28 17:37:21 +0200160 ly_bool out_stdout = 0;
aPiecek2cfeeae2021-04-21 13:17:34 +0200161 size_t line_length = 0;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100162
163 if (parse_cmdline(cmdline, &argc, &argv)) {
164 goto cleanup;
165 }
166
aPiecek2cfeeae2021-04-21 13:17:34 +0200167 while ((opt = getopt_long(argc, argv, "f:hL:o:P:q", options, &opt_index)) != -1) {
Radek Krejcie9f13b12020-11-09 17:42:04 +0100168 switch (opt) {
169 case 'o': /* --output */
170 if (out) {
171 if (ly_out_filepath(out, optarg) != NULL) {
Radek Krejci540ce982020-11-26 12:01:30 +0100172 YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100173 goto cleanup;
174 }
175 } else {
176 if (ly_out_new_filepath(optarg, &out)) {
Radek Krejci540ce982020-11-26 12:01:30 +0100177 YLMSG_E("Unable to use output file %s for printing output.\n", optarg);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100178 goto cleanup;
179 }
180 }
181 break;
182
183 case 'f': /* --format */
184 if (!strcasecmp(optarg, "yang")) {
185 format = LYS_OUT_YANG;
186 } else if (!strcasecmp(optarg, "yin")) {
187 format = LYS_OUT_YIN;
188 } else if (!strcasecmp(optarg, "info")) {
189 format = LYS_OUT_YANG_COMPILED;
190 } else if (!strcasecmp(optarg, "tree")) {
191 format = LYS_OUT_TREE;
192 } else {
193 YLMSG_E("Unknown output format %s\n", optarg);
194 cmd_print_help();
195 goto cleanup;
196 }
197 break;
198
aPiecek2cfeeae2021-04-21 13:17:34 +0200199 case 'L': /* --tree-line-length */
200 line_length = atoi(optarg);
201 break;
202
Radek Krejcie9f13b12020-11-09 17:42:04 +0100203 case 'P': /* --schema-node */
204 node_path = optarg;
205 break;
206
207 case 'q': /* --single-node */
208 options_print |= LYS_PRINT_NO_SUBSTMT;
209 break;
210
211 case 'h':
212 cmd_print_help();
213 goto cleanup;
214 default:
215 YLMSG_E("Unknown option.\n");
216 goto cleanup;
217 }
218 }
219
220 /* file name */
221 if ((argc == optind) && !node_path) {
222 YLMSG_E("Missing the name of the module to print.\n");
223 goto cleanup;
224 }
225
aPiecek2cfeeae2021-04-21 13:17:34 +0200226 if ((format != LYS_OUT_TREE) && line_length) {
227 YLMSG_E("--tree-line-length take effect only in case of the tree output format.\n");
228 goto cleanup;
229 }
230
Radek Krejci540ce982020-11-26 12:01:30 +0100231 if (!out) {
232 if (ly_out_new_file(stdout, &out)) {
233 YLMSG_E("Could not use stdout to print output.\n");
234 goto cleanup;
235 }
Radek Krejci4b0f01b2021-03-28 17:37:21 +0200236 out_stdout = 1;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100237 }
238
aPiecekc1603ba2021-04-19 13:52:22 +0200239 if (format == LYS_OUT_TREE) {
240 /* print tree from lysc_nodes */
241 ly_ctx_set_options(*ctx, LY_CTX_SET_PRIV_PARSED);
242 }
243
Radek Krejcie9f13b12020-11-09 17:42:04 +0100244 if (node_path) {
245 const struct lysc_node *node;
Michal Vasko26bbb272022-08-02 14:54:33 +0200246
romanf00089c2022-10-06 16:01:31 +0200247 node = find_schema_path(*ctx, node_path);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100248 if (!node) {
romanf00089c2022-10-06 16:01:31 +0200249 YLMSG_E("The requested schema node \"%s\" does not exists.\n", node_path);
250 goto cleanup;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100251 }
romanf00089c2022-10-06 16:01:31 +0200252
aPiecek146fd192023-03-30 11:22:16 +0200253 if (lys_print_node(out, node, format, line_length, options_print)) {
Radek Krejcie9f13b12020-11-09 17:42:04 +0100254 YLMSG_E("Unable to print schema node %s.\n", node_path);
255 goto cleanup;
256 }
257 } else {
aPiecek2cfeeae2021-04-21 13:17:34 +0200258 cmd_print_modules(argc, argv, out, ctx, format, line_length, options_print);
aPiecek0bea9872021-04-21 11:13:20 +0200259 goto cleanup;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100260 }
261
262cleanup:
263 free_cmdline(argv);
Radek Krejci4b0f01b2021-03-28 17:37:21 +0200264 ly_out_free(out, NULL, out_stdout ? 0 : 1);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100265}