/** * @file common.c * @author Radek Krejci * @author Adam Piecek * @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 #include #include #include #include #include #include #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; }