blob: d86c54f1bbac9694fe37279e49f0bf9930d06f9a [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>
Michal Vasko1407d7d2023-08-21 09:57:54 +020023#include <stdarg.h>
Radek Krejcie9f13b12020-11-09 17:42:04 +010024#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/stat.h>
28
29#include "compat.h"
30#include "libyang.h"
aPiecek647f62e2023-05-18 10:55:58 +020031#include "plugins_exts.h"
aPieceka83b8e02023-06-07 15:25:16 +020032#include "yl_opt.h"
Radek Krejcie9f13b12020-11-09 17:42:04 +010033
Michal Vasko1407d7d2023-08-21 09:57:54 +020034void
35yl_log(ly_bool err, const char *format, ...)
36{
37 char msg[256];
38 va_list ap;
39
40 va_start(ap, format);
41 vsnprintf(msg, 256, format, ap);
42 va_end(ap);
43
44 fprintf(stderr, "YANGLINT[%c]: %s\n", err ? 'E' : 'W', msg);
45}
46
Radek Krejcie9f13b12020-11-09 17:42:04 +010047int
48parse_schema_path(const char *path, char **dir, char **module)
49{
50 char *p;
51
52 assert(dir);
53 assert(module);
54
55 /* split the path to dirname and basename for further work */
56 *dir = strdup(path);
Jan Kundrát35c145a2023-08-31 21:36:18 +020057 /* FIXME: this is broken on Windows */
Radek Krejcie9f13b12020-11-09 17:42:04 +010058 *module = strrchr(*dir, '/');
59 if (!(*module)) {
60 *module = *dir;
61 *dir = strdup("./");
62 } else {
63 *module[0] = '\0'; /* break the dir */
64 *module = strdup((*module) + 1);
65 }
66 /* get the pure module name without suffix or revision part of the filename */
67 if ((p = strchr(*module, '@'))) {
68 /* revision */
69 *p = '\0';
70 } else if ((p = strrchr(*module, '.'))) {
71 /* fileformat suffix */
72 *p = '\0';
73 }
74
75 return 0;
76}
77
78int
79get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in)
80{
81 struct stat st;
82
83 /* check that the filepath exists and is a regular file */
84 if (stat(filepath, &st) == -1) {
Michal Vasko1407d7d2023-08-21 09:57:54 +020085 YLMSG_E("Unable to use input filepath (%s) - %s.", filepath, strerror(errno));
Radek Krejcie9f13b12020-11-09 17:42:04 +010086 return -1;
87 }
88 if (!S_ISREG(st.st_mode)) {
Michal Vasko1407d7d2023-08-21 09:57:54 +020089 YLMSG_E("Provided input file (%s) is not a regular file.", filepath);
Radek Krejcie9f13b12020-11-09 17:42:04 +010090 return -1;
91 }
92
aPiecekef0a3392023-05-16 10:34:32 +020093 if (get_format(filepath, format_schema, format_data)) {
94 return -1;
Radek Krejcie9f13b12020-11-09 17:42:04 +010095 }
96
aPiecek2457b5e2023-06-12 11:40:14 +020097 if (in && ly_in_new_filepath(filepath, 0, in)) {
Michal Vasko1407d7d2023-08-21 09:57:54 +020098 YLMSG_E("Unable to process input file.");
Radek Krejcie9f13b12020-11-09 17:42:04 +010099 return -1;
100 }
101
102 return 0;
103}
104
aPiecekef0a3392023-05-16 10:34:32 +0200105LYS_INFORMAT
106get_schema_format(const char *filename)
Radek Krejcie9f13b12020-11-09 17:42:04 +0100107{
108 char *ptr;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100109
Radek Krejcie9f13b12020-11-09 17:42:04 +0100110 if ((ptr = strrchr(filename, '.')) != NULL) {
111 ++ptr;
112 if (!strcmp(ptr, "yang")) {
aPiecekef0a3392023-05-16 10:34:32 +0200113 return LYS_IN_YANG;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100114 } else if (!strcmp(ptr, "yin")) {
aPiecekef0a3392023-05-16 10:34:32 +0200115 return LYS_IN_YIN;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100116 } else {
aPiecekef0a3392023-05-16 10:34:32 +0200117 return LYS_IN_UNKNOWN;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100118 }
119 } else {
aPiecekef0a3392023-05-16 10:34:32 +0200120 return LYS_IN_UNKNOWN;
121 }
122}
123
124LYD_FORMAT
125get_data_format(const char *filename)
126{
127 char *ptr;
128
129 if ((ptr = strrchr(filename, '.')) != NULL) {
130 ++ptr;
131 if (!strcmp(ptr, "xml")) {
132 return LYD_XML;
133 } else if (!strcmp(ptr, "json")) {
134 return LYD_JSON;
135 } else if (!strcmp(ptr, "lyb")) {
136 return LYD_LYB;
137 } else {
138 return LYD_UNKNOWN;
139 }
140 } else {
141 return LYD_UNKNOWN;
142 }
143}
144
145int
146get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form)
147{
148 LYS_INFORMAT schema;
149 LYD_FORMAT data;
150
151 schema = !schema_form || !*schema_form ? LYS_IN_UNKNOWN : *schema_form;
152 data = !data_form || !*data_form ? LYD_UNKNOWN : *data_form;
153
154 if (!schema) {
155 schema = get_schema_format(filepath);
156 }
157 if (!data) {
158 data = get_data_format(filepath);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100159 }
160
aPiecekef0a3392023-05-16 10:34:32 +0200161 if (!schema && !data) {
162 YLMSG_E("Input schema format for %s file not recognized.", filepath);
163 return -1;
164 } else if (!data && !schema) {
165 YLMSG_E("Input data format for %s file not recognized.", filepath);
166 return -1;
167 }
168 assert(schema || data);
169
170 if (schema_form) {
171 *schema_form = schema;
172 }
173 if (data_form) {
174 *data_form = data;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100175 }
176
177 return 0;
178}
179
romanf00089c2022-10-06 16:01:31 +0200180const struct lysc_node *
181find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
182{
183 const char *end, *module_name_end;
184 char *module_name = NULL;
Michal Vaskob10c93b2022-12-14 12:16:27 +0100185 const struct lysc_node *node = NULL, *parent_node = NULL, *parent_node_tmp = NULL;
romanf00089c2022-10-06 16:01:31 +0200186 const struct lys_module *module;
187 size_t node_name_len;
188 ly_bool found_exact_match = 0;
189
190 /* iterate over each '/' in the path */
191 while (schema_path) {
192 /* example: schema_path = /listen/endpoint
193 * end == NULL for endpoint, end exists for listen */
194 end = strchr(schema_path + 1, '/');
195 if (end) {
196 node_name_len = end - schema_path - 1;
197 } else {
198 node_name_len = strlen(schema_path + 1);
199 }
200
201 /* ex: schema_path = /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 */
202 module_name_end = strchr(schema_path, ':');
romanba1421b2022-10-07 11:02:36 +0200203 if (module_name_end && (!end || (module_name_end < end))) {
204 /* only get module's name, if it is in the current scope */
205 free(module_name);
206 /* - 1 because module_name_end points to ':' */
207 module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
208 if (!module_name) {
Michal Vasko1407d7d2023-08-21 09:57:54 +0200209 YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
romanba1421b2022-10-07 11:02:36 +0200210 parent_node = NULL;
211 goto cleanup;
romanf00089c2022-10-06 16:01:31 +0200212 }
romanba1421b2022-10-07 11:02:36 +0200213 /* move the pointer to the beginning of the node's name - 1 */
214 schema_path = module_name_end;
romanf00089c2022-10-06 16:01:31 +0200215
romanba1421b2022-10-07 11:02:36 +0200216 /* recalculate the length of the node's name, because the module prefix mustn't be compared later */
romanf00089c2022-10-06 16:01:31 +0200217 if (module_name_end < end) {
romanba1421b2022-10-07 11:02:36 +0200218 node_name_len = end - module_name_end - 1;
219 } else if (!end) {
220 node_name_len = strlen(module_name_end + 1);
romanf00089c2022-10-06 16:01:31 +0200221 }
222 }
223
224 module = ly_ctx_get_module_implemented(ctx, module_name);
225 if (!module) {
226 /* unknown module name */
227 parent_node = NULL;
228 goto cleanup;
229 }
230
231 /* iterate over the node's siblings / module's top level containers */
232 while ((node = lys_getnext(node, parent_node, module->compiled, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
233 if (end && !strncmp(node->name, schema_path + 1, node_name_len) && (node->name[node_name_len] == '\0')) {
234 /* check if the whole node's name matches and it's not just a common prefix */
235 parent_node = node;
236 break;
237 } else if (!strncmp(node->name, schema_path + 1, node_name_len)) {
238 /* do the same here, however if there is no exact match, use the last node with the same prefix */
239 if (strlen(node->name) == node_name_len) {
240 parent_node = node;
241 found_exact_match = 1;
242 break;
243 } else {
244 parent_node_tmp = node;
245 }
246 }
247 }
248
249 if (!end && !found_exact_match) {
250 /* no exact match */
251 parent_node = parent_node_tmp;
252 }
253 found_exact_match = 0;
254
255 /* next iter */
256 schema_path = strchr(schema_path + 1, '/');
257 }
258
259cleanup:
260 free(module_name);
261 return parent_node;
262}
aPiecek647f62e2023-05-18 10:55:58 +0200263
264LY_ERR
265ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
266{
267 struct ly_ctx *ctx;
268 struct lyd_node *data = NULL;
269
270 ctx = ext->module->ctx;
271 if (user_data) {
272 lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
273 }
274
275 *ext_data = data;
276 *ext_data_free = 1;
277 return LY_SUCCESS;
278}
aPiecek21c1bc82023-05-18 15:38:31 +0200279
280LY_ERR
281searchpath_strcat(char **searchpaths, const char *path)
282{
283 uint64_t len;
284 char *new;
285
286 if (!(*searchpaths)) {
287 *searchpaths = strdup(path);
288 return LY_SUCCESS;
289 }
290
291 len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR);
292 new = realloc(*searchpaths, sizeof(char) * len + 1);
293 if (!new) {
294 return LY_EMEM;
295 }
296 strcat(new, PATH_SEPARATOR);
297 strcat(new, path);
298 *searchpaths = new;
299
300 return LY_SUCCESS;
301}