blob: bdf96e6ea5fb5be6fb8325d8ffe5cfbe768cfcca [file] [log] [blame]
Radek Krejcie9f13b12020-11-09 17:42:04 +01001/**
2 * @file common.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
aPieceka83b8e02023-06-07 15:25:16 +02004 * @author Adam Piecek <piecek@cesnet.cz>
Radek Krejcie9f13b12020-11-09 17:42:04 +01005 * @brief libyang's yanglint tool - common functions for both interactive and non-interactive mode.
6 *
aPieceka83b8e02023-06-07 15:25:16 +02007 * Copyright (c) 2023 CESNET, z.s.p.o.
Radek Krejcie9f13b12020-11-09 17:42:04 +01008 *
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
Radek Krejcif8dc59a2020-11-25 13:47:44 +010017#define _POSIX_C_SOURCE 200809L /* strdup, strndup */
Radek Krejcie9f13b12020-11-09 17:42:04 +010018
19#include "common.h"
20
21#include <assert.h>
22#include <errno.h>
Radek Krejcie9f13b12020-11-09 17:42:04 +010023#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27
28#include "compat.h"
29#include "libyang.h"
aPiecek647f62e2023-05-18 10:55:58 +020030#include "plugins_exts.h"
aPieceka83b8e02023-06-07 15:25:16 +020031#include "yl_opt.h"
Radek Krejcie9f13b12020-11-09 17:42:04 +010032
33int
34parse_schema_path(const char *path, char **dir, char **module)
35{
36 char *p;
37
38 assert(dir);
39 assert(module);
40
41 /* split the path to dirname and basename for further work */
42 *dir = strdup(path);
43 *module = strrchr(*dir, '/');
44 if (!(*module)) {
45 *module = *dir;
46 *dir = strdup("./");
47 } else {
48 *module[0] = '\0'; /* break the dir */
49 *module = strdup((*module) + 1);
50 }
51 /* get the pure module name without suffix or revision part of the filename */
52 if ((p = strchr(*module, '@'))) {
53 /* revision */
54 *p = '\0';
55 } else if ((p = strrchr(*module, '.'))) {
56 /* fileformat suffix */
57 *p = '\0';
58 }
59
60 return 0;
61}
62
63int
64get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in)
65{
66 struct stat st;
67
68 /* check that the filepath exists and is a regular file */
69 if (stat(filepath, &st) == -1) {
70 YLMSG_E("Unable to use input filepath (%s) - %s.\n", filepath, strerror(errno));
71 return -1;
72 }
73 if (!S_ISREG(st.st_mode)) {
74 YLMSG_E("Provided input file (%s) is not a regular file.\n", filepath);
75 return -1;
76 }
77
aPiecekef0a3392023-05-16 10:34:32 +020078 if (get_format(filepath, format_schema, format_data)) {
79 return -1;
Radek Krejcie9f13b12020-11-09 17:42:04 +010080 }
81
aPiecek2457b5e2023-06-12 11:40:14 +020082 if (in && ly_in_new_filepath(filepath, 0, in)) {
Radek Krejcie9f13b12020-11-09 17:42:04 +010083 YLMSG_E("Unable to process input file.\n");
84 return -1;
85 }
86
87 return 0;
88}
89
aPiecekef0a3392023-05-16 10:34:32 +020090LYS_INFORMAT
91get_schema_format(const char *filename)
Radek Krejcie9f13b12020-11-09 17:42:04 +010092{
93 char *ptr;
Radek Krejcie9f13b12020-11-09 17:42:04 +010094
Radek Krejcie9f13b12020-11-09 17:42:04 +010095 if ((ptr = strrchr(filename, '.')) != NULL) {
96 ++ptr;
97 if (!strcmp(ptr, "yang")) {
aPiecekef0a3392023-05-16 10:34:32 +020098 return LYS_IN_YANG;
Radek Krejcie9f13b12020-11-09 17:42:04 +010099 } else if (!strcmp(ptr, "yin")) {
aPiecekef0a3392023-05-16 10:34:32 +0200100 return LYS_IN_YIN;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100101 } else {
aPiecekef0a3392023-05-16 10:34:32 +0200102 return LYS_IN_UNKNOWN;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100103 }
104 } else {
aPiecekef0a3392023-05-16 10:34:32 +0200105 return LYS_IN_UNKNOWN;
106 }
107}
108
109LYD_FORMAT
110get_data_format(const char *filename)
111{
112 char *ptr;
113
114 if ((ptr = strrchr(filename, '.')) != NULL) {
115 ++ptr;
116 if (!strcmp(ptr, "xml")) {
117 return LYD_XML;
118 } else if (!strcmp(ptr, "json")) {
119 return LYD_JSON;
120 } else if (!strcmp(ptr, "lyb")) {
121 return LYD_LYB;
122 } else {
123 return LYD_UNKNOWN;
124 }
125 } else {
126 return LYD_UNKNOWN;
127 }
128}
129
130int
131get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form)
132{
133 LYS_INFORMAT schema;
134 LYD_FORMAT data;
135
136 schema = !schema_form || !*schema_form ? LYS_IN_UNKNOWN : *schema_form;
137 data = !data_form || !*data_form ? LYD_UNKNOWN : *data_form;
138
139 if (!schema) {
140 schema = get_schema_format(filepath);
141 }
142 if (!data) {
143 data = get_data_format(filepath);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100144 }
145
aPiecekef0a3392023-05-16 10:34:32 +0200146 if (!schema && !data) {
147 YLMSG_E("Input schema format for %s file not recognized.", filepath);
148 return -1;
149 } else if (!data && !schema) {
150 YLMSG_E("Input data format for %s file not recognized.", filepath);
151 return -1;
152 }
153 assert(schema || data);
154
155 if (schema_form) {
156 *schema_form = schema;
157 }
158 if (data_form) {
159 *data_form = data;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100160 }
161
162 return 0;
163}
164
romanf00089c2022-10-06 16:01:31 +0200165const struct lysc_node *
166find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
167{
168 const char *end, *module_name_end;
169 char *module_name = NULL;
Michal Vaskob10c93b2022-12-14 12:16:27 +0100170 const struct lysc_node *node = NULL, *parent_node = NULL, *parent_node_tmp = NULL;
romanf00089c2022-10-06 16:01:31 +0200171 const struct lys_module *module;
172 size_t node_name_len;
173 ly_bool found_exact_match = 0;
174
175 /* iterate over each '/' in the path */
176 while (schema_path) {
177 /* example: schema_path = /listen/endpoint
178 * end == NULL for endpoint, end exists for listen */
179 end = strchr(schema_path + 1, '/');
180 if (end) {
181 node_name_len = end - schema_path - 1;
182 } else {
183 node_name_len = strlen(schema_path + 1);
184 }
185
186 /* ex: schema_path = /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 */
187 module_name_end = strchr(schema_path, ':');
romanba1421b2022-10-07 11:02:36 +0200188 if (module_name_end && (!end || (module_name_end < end))) {
189 /* only get module's name, if it is in the current scope */
190 free(module_name);
191 /* - 1 because module_name_end points to ':' */
192 module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
193 if (!module_name) {
194 YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
195 parent_node = NULL;
196 goto cleanup;
romanf00089c2022-10-06 16:01:31 +0200197 }
romanba1421b2022-10-07 11:02:36 +0200198 /* move the pointer to the beginning of the node's name - 1 */
199 schema_path = module_name_end;
romanf00089c2022-10-06 16:01:31 +0200200
romanba1421b2022-10-07 11:02:36 +0200201 /* recalculate the length of the node's name, because the module prefix mustn't be compared later */
romanf00089c2022-10-06 16:01:31 +0200202 if (module_name_end < end) {
romanba1421b2022-10-07 11:02:36 +0200203 node_name_len = end - module_name_end - 1;
204 } else if (!end) {
205 node_name_len = strlen(module_name_end + 1);
romanf00089c2022-10-06 16:01:31 +0200206 }
207 }
208
209 module = ly_ctx_get_module_implemented(ctx, module_name);
210 if (!module) {
211 /* unknown module name */
212 parent_node = NULL;
213 goto cleanup;
214 }
215
216 /* iterate over the node's siblings / module's top level containers */
217 while ((node = lys_getnext(node, parent_node, module->compiled, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
218 if (end && !strncmp(node->name, schema_path + 1, node_name_len) && (node->name[node_name_len] == '\0')) {
219 /* check if the whole node's name matches and it's not just a common prefix */
220 parent_node = node;
221 break;
222 } else if (!strncmp(node->name, schema_path + 1, node_name_len)) {
223 /* do the same here, however if there is no exact match, use the last node with the same prefix */
224 if (strlen(node->name) == node_name_len) {
225 parent_node = node;
226 found_exact_match = 1;
227 break;
228 } else {
229 parent_node_tmp = node;
230 }
231 }
232 }
233
234 if (!end && !found_exact_match) {
235 /* no exact match */
236 parent_node = parent_node_tmp;
237 }
238 found_exact_match = 0;
239
240 /* next iter */
241 schema_path = strchr(schema_path + 1, '/');
242 }
243
244cleanup:
245 free(module_name);
246 return parent_node;
247}
aPiecek647f62e2023-05-18 10:55:58 +0200248
249LY_ERR
250ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
251{
252 struct ly_ctx *ctx;
253 struct lyd_node *data = NULL;
254
255 ctx = ext->module->ctx;
256 if (user_data) {
257 lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
258 }
259
260 *ext_data = data;
261 *ext_data_free = 1;
262 return LY_SUCCESS;
263}
aPiecek21c1bc82023-05-18 15:38:31 +0200264
265LY_ERR
266searchpath_strcat(char **searchpaths, const char *path)
267{
268 uint64_t len;
269 char *new;
270
271 if (!(*searchpaths)) {
272 *searchpaths = strdup(path);
273 return LY_SUCCESS;
274 }
275
276 len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR);
277 new = realloc(*searchpaths, sizeof(char) * len + 1);
278 if (!new) {
279 return LY_EMEM;
280 }
281 strcat(new, PATH_SEPARATOR);
282 strcat(new, path);
283 *searchpaths = new;
284
285 return LY_SUCCESS;
286}