diff options
Diffstat (limited to 'tools/lint/common.c')
-rw-r--r-- | tools/lint/common.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/tools/lint/common.c b/tools/lint/common.c new file mode 100644 index 0000000..d86c54f --- /dev/null +++ b/tools/lint/common.c @@ -0,0 +1,301 @@ +/** + * @file common.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Adam Piecek <piecek@cesnet.cz> + * @brief libyang's yanglint tool - common functions for both interactive and non-interactive mode. + * + * Copyright (c) 2023 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ + +#define _GNU_SOURCE +#define _POSIX_C_SOURCE 200809L /* strdup, strndup */ + +#include "common.h" + +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include "compat.h" +#include "libyang.h" +#include "plugins_exts.h" +#include "yl_opt.h" + +void +yl_log(ly_bool err, const char *format, ...) +{ + char msg[256]; + va_list ap; + + va_start(ap, format); + vsnprintf(msg, 256, format, ap); + va_end(ap); + + fprintf(stderr, "YANGLINT[%c]: %s\n", err ? 'E' : 'W', msg); +} + +int +parse_schema_path(const char *path, char **dir, char **module) +{ + char *p; + + assert(dir); + assert(module); + + /* split the path to dirname and basename for further work */ + *dir = strdup(path); + /* FIXME: this is broken on Windows */ + *module = strrchr(*dir, '/'); + if (!(*module)) { + *module = *dir; + *dir = strdup("./"); + } else { + *module[0] = '\0'; /* break the dir */ + *module = strdup((*module) + 1); + } + /* get the pure module name without suffix or revision part of the filename */ + if ((p = strchr(*module, '@'))) { + /* revision */ + *p = '\0'; + } else if ((p = strrchr(*module, '.'))) { + /* fileformat suffix */ + *p = '\0'; + } + + return 0; +} + +int +get_input(const char *filepath, LYS_INFORMAT *format_schema, LYD_FORMAT *format_data, struct ly_in **in) +{ + struct stat st; + + /* check that the filepath exists and is a regular file */ + if (stat(filepath, &st) == -1) { + YLMSG_E("Unable to use input filepath (%s) - %s.", filepath, strerror(errno)); + return -1; + } + if (!S_ISREG(st.st_mode)) { + YLMSG_E("Provided input file (%s) is not a regular file.", filepath); + return -1; + } + + if (get_format(filepath, format_schema, format_data)) { + return -1; + } + + if (in && ly_in_new_filepath(filepath, 0, in)) { + YLMSG_E("Unable to process input file."); + return -1; + } + + return 0; +} + +LYS_INFORMAT +get_schema_format(const char *filename) +{ + char *ptr; + + if ((ptr = strrchr(filename, '.')) != NULL) { + ++ptr; + if (!strcmp(ptr, "yang")) { + return LYS_IN_YANG; + } else if (!strcmp(ptr, "yin")) { + return LYS_IN_YIN; + } else { + return LYS_IN_UNKNOWN; + } + } else { + return LYS_IN_UNKNOWN; + } +} + +LYD_FORMAT +get_data_format(const char *filename) +{ + char *ptr; + + if ((ptr = strrchr(filename, '.')) != NULL) { + ++ptr; + if (!strcmp(ptr, "xml")) { + return LYD_XML; + } else if (!strcmp(ptr, "json")) { + return LYD_JSON; + } else if (!strcmp(ptr, "lyb")) { + return LYD_LYB; + } else { + return LYD_UNKNOWN; + } + } else { + return LYD_UNKNOWN; + } +} + +int +get_format(const char *filepath, LYS_INFORMAT *schema_form, LYD_FORMAT *data_form) +{ + LYS_INFORMAT schema; + LYD_FORMAT data; + + schema = !schema_form || !*schema_form ? LYS_IN_UNKNOWN : *schema_form; + data = !data_form || !*data_form ? LYD_UNKNOWN : *data_form; + + if (!schema) { + schema = get_schema_format(filepath); + } + if (!data) { + data = get_data_format(filepath); + } + + if (!schema && !data) { + YLMSG_E("Input schema format for %s file not recognized.", filepath); + return -1; + } else if (!data && !schema) { + YLMSG_E("Input data format for %s file not recognized.", filepath); + return -1; + } + assert(schema || data); + + if (schema_form) { + *schema_form = schema; + } + if (data_form) { + *data_form = data; + } + + return 0; +} + +const struct lysc_node * +find_schema_path(const struct ly_ctx *ctx, const char *schema_path) +{ + const char *end, *module_name_end; + char *module_name = NULL; + const struct lysc_node *node = NULL, *parent_node = NULL, *parent_node_tmp = NULL; + const struct lys_module *module; + size_t node_name_len; + ly_bool found_exact_match = 0; + + /* iterate over each '/' in the path */ + while (schema_path) { + /* example: schema_path = /listen/endpoint + * end == NULL for endpoint, end exists for listen */ + end = strchr(schema_path + 1, '/'); + if (end) { + node_name_len = end - schema_path - 1; + } else { + node_name_len = strlen(schema_path + 1); + } + + /* ex: schema_path = /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 */ + module_name_end = strchr(schema_path, ':'); + if (module_name_end && (!end || (module_name_end < end))) { + /* only get module's name, if it is in the current scope */ + free(module_name); + /* - 1 because module_name_end points to ':' */ + module_name = strndup(schema_path + 1, module_name_end - schema_path - 1); + if (!module_name) { + YLMSG_E("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno)); + parent_node = NULL; + goto cleanup; + } + /* move the pointer to the beginning of the node's name - 1 */ + schema_path = module_name_end; + + /* recalculate the length of the node's name, because the module prefix mustn't be compared later */ + if (module_name_end < end) { + node_name_len = end - module_name_end - 1; + } else if (!end) { + node_name_len = strlen(module_name_end + 1); + } + } + + module = ly_ctx_get_module_implemented(ctx, module_name); + if (!module) { + /* unknown module name */ + parent_node = NULL; + goto cleanup; + } + + /* iterate over the node's siblings / module's top level containers */ + while ((node = lys_getnext(node, parent_node, module->compiled, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) { + if (end && !strncmp(node->name, schema_path + 1, node_name_len) && (node->name[node_name_len] == '\0')) { + /* check if the whole node's name matches and it's not just a common prefix */ + parent_node = node; + break; + } else if (!strncmp(node->name, schema_path + 1, node_name_len)) { + /* do the same here, however if there is no exact match, use the last node with the same prefix */ + if (strlen(node->name) == node_name_len) { + parent_node = node; + found_exact_match = 1; + break; + } else { + parent_node_tmp = node; + } + } + } + + if (!end && !found_exact_match) { + /* no exact match */ + parent_node = parent_node_tmp; + } + found_exact_match = 0; + + /* next iter */ + schema_path = strchr(schema_path + 1, '/'); + } + +cleanup: + free(module_name); + return parent_node; +} + +LY_ERR +ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free) +{ + struct ly_ctx *ctx; + struct lyd_node *data = NULL; + + ctx = ext->module->ctx; + if (user_data) { + lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data); + } + + *ext_data = data; + *ext_data_free = 1; + return LY_SUCCESS; +} + +LY_ERR +searchpath_strcat(char **searchpaths, const char *path) +{ + uint64_t len; + char *new; + + if (!(*searchpaths)) { + *searchpaths = strdup(path); + return LY_SUCCESS; + } + + len = strlen(*searchpaths) + strlen(path) + strlen(PATH_SEPARATOR); + new = realloc(*searchpaths, sizeof(char) * len + 1); + if (!new) { + return LY_EMEM; + } + strcat(new, PATH_SEPARATOR); + strcat(new, path); + *searchpaths = new; + + return LY_SUCCESS; +} |