blob: 08d23c3e0067756ff566d72c03ffaf443c2f2e22 [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);
57 *module = strrchr(*dir, '/');
58 if (!(*module)) {
59 *module = *dir;
60 *dir = strdup("./");
61 } else {
62 *module[0] = '\0'; /* break the dir */
63 *module = strdup((*module) + 1);
64 }
65 /* get the pure module name without suffix or revision part of the filename */
66 if ((p = strchr(*module, '@'))) {
67 /* revision */
68 *p = '\0';
69 } else if ((p = strrchr(*module, '.'))) {
70 /* fileformat suffix */
71 *p = '\0';
72 }
73
74 return 0;
75}
76
77int
78get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in)
79{
80 struct stat st;
81
82 /* check that the filepath exists and is a regular file */
83 if (stat(filepath, &st) == -1) {
Michal Vasko1407d7d2023-08-21 09:57:54 +020084 YLMSG_E("Unable to use input filepath (%s) - %s.", filepath, strerror(errno));
Radek Krejcie9f13b12020-11-09 17:42:04 +010085 return -1;
86 }
87 if (!S_ISREG(st.st_mode)) {
Michal Vasko1407d7d2023-08-21 09:57:54 +020088 YLMSG_E("Provided input file (%s) is not a regular file.", filepath);
Radek Krejcie9f13b12020-11-09 17:42:04 +010089 return -1;
90 }
91
aPiecekef0a3392023-05-16 10:34:32 +020092 if (get_format(filepath, format_schema, format_data)) {
93 return -1;
Radek Krejcie9f13b12020-11-09 17:42:04 +010094 }
95
aPiecek2457b5e2023-06-12 11:40:14 +020096 if (in && ly_in_new_filepath(filepath, 0, in)) {
Michal Vasko1407d7d2023-08-21 09:57:54 +020097 YLMSG_E("Unable to process input file.");
Radek Krejcie9f13b12020-11-09 17:42:04 +010098 return -1;
99 }
100
101 return 0;
102}
103
aPiecekef0a3392023-05-16 10:34:32 +0200104LYS_INFORMAT
105get_schema_format(const char *filename)
Radek Krejcie9f13b12020-11-09 17:42:04 +0100106{
107 char *ptr;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100108
Radek Krejcie9f13b12020-11-09 17:42:04 +0100109 if ((ptr = strrchr(filename, '.')) != NULL) {
110 ++ptr;
111 if (!strcmp(ptr, "yang")) {
aPiecekef0a3392023-05-16 10:34:32 +0200112 return LYS_IN_YANG;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100113 } else if (!strcmp(ptr, "yin")) {
aPiecekef0a3392023-05-16 10:34:32 +0200114 return LYS_IN_YIN;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100115 } else {
aPiecekef0a3392023-05-16 10:34:32 +0200116 return LYS_IN_UNKNOWN;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100117 }
118 } else {
aPiecekef0a3392023-05-16 10:34:32 +0200119 return LYS_IN_UNKNOWN;
120 }
121}
122
123LYD_FORMAT
124get_data_format(const char *filename)
125{
126 char *ptr;
127
128 if ((ptr = strrchr(filename, '.')) != NULL) {
129 ++ptr;
130 if (!strcmp(ptr, "xml")) {
131 return LYD_XML;
132 } else if (!strcmp(ptr, "json")) {
133 return LYD_JSON;
134 } else if (!strcmp(ptr, "lyb")) {
135 return LYD_LYB;
136 } else {
137 return LYD_UNKNOWN;
138 }
139 } else {
140 return LYD_UNKNOWN;
141 }
142}
143
144int
145get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form)
146{
147 LYS_INFORMAT schema;
148 LYD_FORMAT data;
149
150 schema = !schema_form || !*schema_form ? LYS_IN_UNKNOWN : *schema_form;
151 data = !data_form || !*data_form ? LYD_UNKNOWN : *data_form;
152
153 if (!schema) {
154 schema = get_schema_format(filepath);
155 }
156 if (!data) {
157 data = get_data_format(filepath);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100158 }
159
aPiecekef0a3392023-05-16 10:34:32 +0200160 if (!schema && !data) {
161 YLMSG_E("Input schema format for %s file not recognized.", filepath);
162 return -1;
163 } else if (!data && !schema) {
164 YLMSG_E("Input data format for %s file not recognized.", filepath);
165 return -1;
166 }
167 assert(schema || data);
168
169 if (schema_form) {
170 *schema_form = schema;
171 }
172 if (data_form) {
173 *data_form = data;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100174 }
175
176 return 0;
177}
178
romanf00089c2022-10-06 16:01:31 +0200179const struct lysc_node *
180find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
181{
182 const char *end, *module_name_end;
183 char *module_name = NULL;
Michal Vaskob10c93b2022-12-14 12:16:27 +0100184 const struct lysc_node *node = NULL, *parent_node = NULL, *parent_node_tmp = NULL;
romanf00089c2022-10-06 16:01:31 +0200185 const struct lys_module *module;
186 size_t node_name_len;
187 ly_bool found_exact_match = 0;
188
189 /* iterate over each '/' in the path */
190 while (schema_path) {
191 /* example: schema_path = /listen/endpoint
192 * end == NULL for endpoint, end exists for listen */
193 end = strchr(schema_path + 1, '/');
194 if (end) {
195 node_name_len = end - schema_path - 1;
196 } else {
197 node_name_len = strlen(schema_path + 1);
198 }
199
200 /* ex: schema_path = /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 */
201 module_name_end = strchr(schema_path, ':');
romanba1421b2022-10-07 11:02:36 +0200202 if (module_name_end && (!end || (module_name_end < end))) {
203 /* only get module's name, if it is in the current scope */
204 free(module_name);
205 /* - 1 because module_name_end points to ':' */
206 module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
207 if (!module_name) {
Michal Vasko1407d7d2023-08-21 09:57:54 +0200208 YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
romanba1421b2022-10-07 11:02:36 +0200209 parent_node = NULL;
210 goto cleanup;
romanf00089c2022-10-06 16:01:31 +0200211 }
romanba1421b2022-10-07 11:02:36 +0200212 /* move the pointer to the beginning of the node's name - 1 */
213 schema_path = module_name_end;
romanf00089c2022-10-06 16:01:31 +0200214
romanba1421b2022-10-07 11:02:36 +0200215 /* recalculate the length of the node's name, because the module prefix mustn't be compared later */
romanf00089c2022-10-06 16:01:31 +0200216 if (module_name_end < end) {
romanba1421b2022-10-07 11:02:36 +0200217 node_name_len = end - module_name_end - 1;
218 } else if (!end) {
219 node_name_len = strlen(module_name_end + 1);
romanf00089c2022-10-06 16:01:31 +0200220 }
221 }
222
223 module = ly_ctx_get_module_implemented(ctx, module_name);
224 if (!module) {
225 /* unknown module name */
226 parent_node = NULL;
227 goto cleanup;
228 }
229
230 /* iterate over the node's siblings / module's top level containers */
231 while ((node = lys_getnext(node, parent_node, module->compiled, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
232 if (end && !strncmp(node->name, schema_path + 1, node_name_len) && (node->name[node_name_len] == '\0')) {
233 /* check if the whole node's name matches and it's not just a common prefix */
234 parent_node = node;
235 break;
236 } else if (!strncmp(node->name, schema_path + 1, node_name_len)) {
237 /* do the same here, however if there is no exact match, use the last node with the same prefix */
238 if (strlen(node->name) == node_name_len) {
239 parent_node = node;
240 found_exact_match = 1;
241 break;
242 } else {
243 parent_node_tmp = node;
244 }
245 }
246 }
247
248 if (!end && !found_exact_match) {
249 /* no exact match */
250 parent_node = parent_node_tmp;
251 }
252 found_exact_match = 0;
253
254 /* next iter */
255 schema_path = strchr(schema_path + 1, '/');
256 }
257
258cleanup:
259 free(module_name);
260 return parent_node;
261}
aPiecek647f62e2023-05-18 10:55:58 +0200262
263LY_ERR
264ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
265{
266 struct ly_ctx *ctx;
267 struct lyd_node *data = NULL;
268
269 ctx = ext->module->ctx;
270 if (user_data) {
271 lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
272 }
273
274 *ext_data = data;
275 *ext_data_free = 1;
276 return LY_SUCCESS;
277}
aPiecek21c1bc82023-05-18 15:38:31 +0200278
279LY_ERR
280searchpath_strcat(char **searchpaths, const char *path)
281{
282 uint64_t len;
283 char *new;
284
285 if (!(*searchpaths)) {
286 *searchpaths = strdup(path);
287 return LY_SUCCESS;
288 }
289
290 len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR);
291 new = realloc(*searchpaths, sizeof(char) * len + 1);
292 if (!new) {
293 return LY_EMEM;
294 }
295 strcat(new, PATH_SEPARATOR);
296 strcat(new, path);
297 *searchpaths = new;
298
299 return LY_SUCCESS;
300}