3942 lines
138 KiB
C
3942 lines
138 KiB
C
|
/**
|
||
|
* @file parser.c
|
||
|
* @author Radek Krejci <rkrejci@cesnet.cz>
|
||
|
* @brief common libyang parsers routines implementations
|
||
|
*
|
||
|
* Copyright (c) 2015-2017 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
|
||
|
#include <assert.h>
|
||
|
#include <ctype.h>
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <inttypes.h>
|
||
|
#include <limits.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/mman.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <unistd.h>
|
||
|
#include <pcre.h>
|
||
|
#include <time.h>
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "context.h"
|
||
|
#include "libyang.h"
|
||
|
#include "parser.h"
|
||
|
#include "resolve.h"
|
||
|
#include "tree_internal.h"
|
||
|
#include "parser_yang.h"
|
||
|
#include "xpath.h"
|
||
|
|
||
|
#define LYP_URANGE_LEN 19
|
||
|
|
||
|
static char *lyp_ublock2urange[][2] = {
|
||
|
{"BasicLatin", "[\\x{0000}-\\x{007F}]"},
|
||
|
{"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"},
|
||
|
{"LatinExtended-A", "[\\x{0100}-\\x{017F}]"},
|
||
|
{"LatinExtended-B", "[\\x{0180}-\\x{024F}]"},
|
||
|
{"IPAExtensions", "[\\x{0250}-\\x{02AF}]"},
|
||
|
{"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"},
|
||
|
{"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"},
|
||
|
{"Greek", "[\\x{0370}-\\x{03FF}]"},
|
||
|
{"Cyrillic", "[\\x{0400}-\\x{04FF}]"},
|
||
|
{"Armenian", "[\\x{0530}-\\x{058F}]"},
|
||
|
{"Hebrew", "[\\x{0590}-\\x{05FF}]"},
|
||
|
{"Arabic", "[\\x{0600}-\\x{06FF}]"},
|
||
|
{"Syriac", "[\\x{0700}-\\x{074F}]"},
|
||
|
{"Thaana", "[\\x{0780}-\\x{07BF}]"},
|
||
|
{"Devanagari", "[\\x{0900}-\\x{097F}]"},
|
||
|
{"Bengali", "[\\x{0980}-\\x{09FF}]"},
|
||
|
{"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"},
|
||
|
{"Gujarati", "[\\x{0A80}-\\x{0AFF}]"},
|
||
|
{"Oriya", "[\\x{0B00}-\\x{0B7F}]"},
|
||
|
{"Tamil", "[\\x{0B80}-\\x{0BFF}]"},
|
||
|
{"Telugu", "[\\x{0C00}-\\x{0C7F}]"},
|
||
|
{"Kannada", "[\\x{0C80}-\\x{0CFF}]"},
|
||
|
{"Malayalam", "[\\x{0D00}-\\x{0D7F}]"},
|
||
|
{"Sinhala", "[\\x{0D80}-\\x{0DFF}]"},
|
||
|
{"Thai", "[\\x{0E00}-\\x{0E7F}]"},
|
||
|
{"Lao", "[\\x{0E80}-\\x{0EFF}]"},
|
||
|
{"Tibetan", "[\\x{0F00}-\\x{0FFF}]"},
|
||
|
{"Myanmar", "[\\x{1000}-\\x{109F}]"},
|
||
|
{"Georgian", "[\\x{10A0}-\\x{10FF}]"},
|
||
|
{"HangulJamo", "[\\x{1100}-\\x{11FF}]"},
|
||
|
{"Ethiopic", "[\\x{1200}-\\x{137F}]"},
|
||
|
{"Cherokee", "[\\x{13A0}-\\x{13FF}]"},
|
||
|
{"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"},
|
||
|
{"Ogham", "[\\x{1680}-\\x{169F}]"},
|
||
|
{"Runic", "[\\x{16A0}-\\x{16FF}]"},
|
||
|
{"Khmer", "[\\x{1780}-\\x{17FF}]"},
|
||
|
{"Mongolian", "[\\x{1800}-\\x{18AF}]"},
|
||
|
{"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"},
|
||
|
{"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"},
|
||
|
{"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"},
|
||
|
{"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"},
|
||
|
{"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"},
|
||
|
{"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"},
|
||
|
{"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"},
|
||
|
{"NumberForms", "[\\x{2150}-\\x{218F}]"},
|
||
|
{"Arrows", "[\\x{2190}-\\x{21FF}]"},
|
||
|
{"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"},
|
||
|
{"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"},
|
||
|
{"ControlPictures", "[\\x{2400}-\\x{243F}]"},
|
||
|
{"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"},
|
||
|
{"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"},
|
||
|
{"BoxDrawing", "[\\x{2500}-\\x{257F}]"},
|
||
|
{"BlockElements", "[\\x{2580}-\\x{259F}]"},
|
||
|
{"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"},
|
||
|
{"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"},
|
||
|
{"Dingbats", "[\\x{2700}-\\x{27BF}]"},
|
||
|
{"BraillePatterns", "[\\x{2800}-\\x{28FF}]"},
|
||
|
{"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"},
|
||
|
{"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"},
|
||
|
{"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"},
|
||
|
{"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"},
|
||
|
{"Hiragana", "[\\x{3040}-\\x{309F}]"},
|
||
|
{"Katakana", "[\\x{30A0}-\\x{30FF}]"},
|
||
|
{"Bopomofo", "[\\x{3100}-\\x{312F}]"},
|
||
|
{"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"},
|
||
|
{"Kanbun", "[\\x{3190}-\\x{319F}]"},
|
||
|
{"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"},
|
||
|
{"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"},
|
||
|
{"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"},
|
||
|
{"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"},
|
||
|
{"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"},
|
||
|
{"YiSyllables", "[\\x{A000}-\\x{A48F}]"},
|
||
|
{"YiRadicals", "[\\x{A490}-\\x{A4CF}]"},
|
||
|
{"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"},
|
||
|
{"PrivateUse", "[\\x{E000}-\\x{F8FF}]"},
|
||
|
{"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"},
|
||
|
{"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"},
|
||
|
{"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"},
|
||
|
{"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"},
|
||
|
{"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"},
|
||
|
{"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"},
|
||
|
{"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"},
|
||
|
{"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"},
|
||
|
{NULL, NULL}
|
||
|
};
|
||
|
|
||
|
const char *ly_stmt_str[] = {
|
||
|
[LY_STMT_UNKNOWN] = "",
|
||
|
[LY_STMT_ARGUMENT] = "argument",
|
||
|
[LY_STMT_BASE] = "base",
|
||
|
[LY_STMT_BELONGSTO] = "belongs-to",
|
||
|
[LY_STMT_CONTACT] = "contact",
|
||
|
[LY_STMT_DEFAULT] = "default",
|
||
|
[LY_STMT_DESCRIPTION] = "description",
|
||
|
[LY_STMT_ERRTAG] = "error-app-tag",
|
||
|
[LY_STMT_ERRMSG] = "error-message",
|
||
|
[LY_STMT_KEY] = "key",
|
||
|
[LY_STMT_NAMESPACE] = "namespace",
|
||
|
[LY_STMT_ORGANIZATION] = "organization",
|
||
|
[LY_STMT_PATH] = "path",
|
||
|
[LY_STMT_PREFIX] = "prefix",
|
||
|
[LY_STMT_PRESENCE] = "presence",
|
||
|
[LY_STMT_REFERENCE] = "reference",
|
||
|
[LY_STMT_REVISIONDATE] = "revision-date",
|
||
|
[LY_STMT_UNITS] = "units",
|
||
|
[LY_STMT_VALUE] = "value",
|
||
|
[LY_STMT_VERSION] = "yang-version",
|
||
|
[LY_STMT_MODIFIER] = "modifier",
|
||
|
[LY_STMT_REQINSTANCE] = "require-instance",
|
||
|
[LY_STMT_YINELEM] = "yin-element",
|
||
|
[LY_STMT_CONFIG] = "config",
|
||
|
[LY_STMT_MANDATORY] = "mandatory",
|
||
|
[LY_STMT_ORDEREDBY] = "ordered-by",
|
||
|
[LY_STMT_STATUS] = "status",
|
||
|
[LY_STMT_DIGITS] = "fraction-digits",
|
||
|
[LY_STMT_MAX] = "max-elements",
|
||
|
[LY_STMT_MIN] = "min-elements",
|
||
|
[LY_STMT_POSITION] = "position",
|
||
|
[LY_STMT_UNIQUE] = "unique",
|
||
|
[LY_STMT_MODULE] = "module",
|
||
|
[LY_STMT_SUBMODULE] = "submodule",
|
||
|
[LY_STMT_ACTION] = "action",
|
||
|
[LY_STMT_ANYDATA] = "anydata",
|
||
|
[LY_STMT_ANYXML] = "anyxml",
|
||
|
[LY_STMT_CASE] = "case",
|
||
|
[LY_STMT_CHOICE] = "choice",
|
||
|
[LY_STMT_CONTAINER] = "container",
|
||
|
[LY_STMT_GROUPING] = "grouping",
|
||
|
[LY_STMT_INPUT] = "input",
|
||
|
[LY_STMT_LEAF] = "leaf",
|
||
|
[LY_STMT_LEAFLIST] = "leaf-list",
|
||
|
[LY_STMT_LIST] = "list",
|
||
|
[LY_STMT_NOTIFICATION] = "notification",
|
||
|
[LY_STMT_OUTPUT] = "output",
|
||
|
[LY_STMT_RPC] = "rpc",
|
||
|
[LY_STMT_USES] = "uses",
|
||
|
[LY_STMT_TYPEDEF] = "typedef",
|
||
|
[LY_STMT_TYPE] = "type",
|
||
|
[LY_STMT_BIT] = "bit",
|
||
|
[LY_STMT_ENUM] = "enum",
|
||
|
[LY_STMT_REFINE] = "refine",
|
||
|
[LY_STMT_AUGMENT] = "augment",
|
||
|
[LY_STMT_DEVIATE] = "deviate",
|
||
|
[LY_STMT_DEVIATION] = "deviation",
|
||
|
[LY_STMT_EXTENSION] = "extension",
|
||
|
[LY_STMT_FEATURE] = "feature",
|
||
|
[LY_STMT_IDENTITY] = "identity",
|
||
|
[LY_STMT_IFFEATURE] = "if-feature",
|
||
|
[LY_STMT_IMPORT] = "import",
|
||
|
[LY_STMT_INCLUDE] = "include",
|
||
|
[LY_STMT_LENGTH] = "length",
|
||
|
[LY_STMT_MUST] = "must",
|
||
|
[LY_STMT_PATTERN] = "pattern",
|
||
|
[LY_STMT_RANGE] = "range",
|
||
|
[LY_STMT_WHEN] = "when",
|
||
|
[LY_STMT_REVISION] = "revision"
|
||
|
};
|
||
|
|
||
|
int
|
||
|
lyp_is_rpc_action(struct lys_node *node)
|
||
|
{
|
||
|
assert(node);
|
||
|
|
||
|
while (lys_parent(node)) {
|
||
|
node = lys_parent(node);
|
||
|
if (node->nodetype == LYS_ACTION) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (node->nodetype & (LYS_RPC | LYS_ACTION)) {
|
||
|
return 1;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_data_check_options(struct ly_ctx *ctx, int options, const char *func)
|
||
|
{
|
||
|
int x = options & LYD_OPT_TYPEMASK;
|
||
|
|
||
|
/* LYD_OPT_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG */
|
||
|
if (options & LYD_OPT_WHENAUTODEL) {
|
||
|
if ((x == LYD_OPT_EDIT) || (x == LYD_OPT_NOTIF_FILTER)) {
|
||
|
LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_WHENAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG)",
|
||
|
func, options);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (options & (LYD_OPT_DATA_ADD_YANGLIB | LYD_OPT_DATA_NO_YANGLIB)) {
|
||
|
if (x != LYD_OPT_DATA) {
|
||
|
LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (LYD_OPT_DATA_*_YANGLIB can be used only with LYD_OPT_DATA)",
|
||
|
func, options);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* "is power of 2" algorithm, with 0 exception */
|
||
|
if (x && !(x && !(x & (x - 1)))) {
|
||
|
LOGERR(ctx, LY_EINVAL, "%s: Invalid options 0x%x (multiple data type flags set).", func, options);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_mmap(struct ly_ctx *ctx, int fd, size_t addsize, size_t *length, void **addr)
|
||
|
{
|
||
|
struct stat sb;
|
||
|
long pagesize;
|
||
|
size_t m;
|
||
|
|
||
|
assert(fd >= 0);
|
||
|
if (fstat(fd, &sb) == -1) {
|
||
|
LOGERR(ctx, LY_ESYS, "Failed to stat the file descriptor (%s) for the mmap().", strerror(errno));
|
||
|
return 1;
|
||
|
}
|
||
|
if (!S_ISREG(sb.st_mode)) {
|
||
|
LOGERR(ctx, LY_EINVAL, "File to mmap() is not a regular file.");
|
||
|
return 1;
|
||
|
}
|
||
|
if (!sb.st_size) {
|
||
|
*addr = NULL;
|
||
|
return 0;
|
||
|
}
|
||
|
pagesize = sysconf(_SC_PAGESIZE);
|
||
|
++addsize; /* at least one additional byte for terminating NULL byte */
|
||
|
|
||
|
m = sb.st_size % pagesize;
|
||
|
if (m && pagesize - m >= addsize) {
|
||
|
/* there will be enough space after the file content mapping to provide zeroed additional bytes */
|
||
|
*length = sb.st_size + addsize;
|
||
|
*addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||
|
} else {
|
||
|
/* there will not be enough bytes after the file content mapping for the additional bytes and some of them
|
||
|
* would overflow into another page that would not be zeroed and any access into it would generate SIGBUS.
|
||
|
* Therefore we have to do the following hack with double mapping. First, the required number of bytes
|
||
|
* (including the additional bytes) is required as anonymous and thus they will be really provided (actually more
|
||
|
* because of using whole pages) and also initialized by zeros. Then, the file is mapped to the same address
|
||
|
* where the anonymous mapping starts. */
|
||
|
*length = sb.st_size + pagesize;
|
||
|
*addr = mmap(NULL, *length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||
|
*addr = mmap(*addr, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd, 0);
|
||
|
}
|
||
|
if (*addr == MAP_FAILED) {
|
||
|
LOGERR(ctx, LY_ESYS, "mmap() failed (%s).", strerror(errno));
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_munmap(void *addr, size_t length)
|
||
|
{
|
||
|
return munmap(addr, length);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_add_ietf_netconf_annotations_config(struct lys_module *mod)
|
||
|
{
|
||
|
void *reallocated;
|
||
|
struct lys_ext_instance_complex *op;
|
||
|
struct lys_type **type;
|
||
|
struct lys_node_anydata *anyxml;
|
||
|
int i;
|
||
|
struct ly_ctx *ctx = mod->ctx; /* shortcut */
|
||
|
|
||
|
reallocated = realloc(mod->ext, (mod->ext_size + 3) * sizeof *mod->ext);
|
||
|
LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
mod->ext = reallocated;
|
||
|
/* 1) edit-config's operation */
|
||
|
op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t));
|
||
|
LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
mod->ext[mod->ext_size] = (struct lys_ext_instance *)op;
|
||
|
op->arg_value = lydict_insert(ctx, "operation", 9);
|
||
|
op->def = &ctx->models.list[0]->extensions[0];
|
||
|
op->ext_type = LYEXT_COMPLEX;
|
||
|
op->module = op->parent = mod;
|
||
|
op->parent_type = LYEXT_PAR_MODULE;
|
||
|
op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt;
|
||
|
op->nodetype = LYS_EXT;
|
||
|
type = (struct lys_type**)&op->content; /* type is stored at offset 0 */
|
||
|
*type = calloc(1, sizeof(struct lys_type));
|
||
|
LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
(*type)->base = LY_TYPE_ENUM;
|
||
|
(*type)->der = ly_types[LY_TYPE_ENUM];
|
||
|
(*type)->parent = (struct lys_tpdf *)op;
|
||
|
(*type)->info.enums.count = 5;
|
||
|
(*type)->info.enums.enm = calloc(5, sizeof *(*type)->info.enums.enm);
|
||
|
LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
(*type)->info.enums.enm[0].value = 0;
|
||
|
(*type)->info.enums.enm[0].name = lydict_insert(ctx, "merge", 5);
|
||
|
(*type)->info.enums.enm[1].value = 1;
|
||
|
(*type)->info.enums.enm[1].name = lydict_insert(ctx, "replace", 7);
|
||
|
(*type)->info.enums.enm[2].value = 2;
|
||
|
(*type)->info.enums.enm[2].name = lydict_insert(ctx, "create", 6);
|
||
|
(*type)->info.enums.enm[3].value = 3;
|
||
|
(*type)->info.enums.enm[3].name = lydict_insert(ctx, "delete", 6);
|
||
|
(*type)->info.enums.enm[4].value = 4;
|
||
|
(*type)->info.enums.enm[4].name = lydict_insert(ctx, "remove", 6);
|
||
|
mod->ext_size++;
|
||
|
|
||
|
/* 2) filter's type */
|
||
|
op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t));
|
||
|
LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
mod->ext[mod->ext_size] = (struct lys_ext_instance *)op;
|
||
|
op->arg_value = lydict_insert(ctx, "type", 4);
|
||
|
op->def = &ctx->models.list[0]->extensions[0];
|
||
|
op->ext_type = LYEXT_COMPLEX;
|
||
|
op->module = op->parent = mod;
|
||
|
op->parent_type = LYEXT_PAR_MODULE;
|
||
|
op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt;
|
||
|
op->nodetype = LYS_EXT;
|
||
|
type = (struct lys_type**)&op->content; /* type is stored at offset 0 */
|
||
|
*type = calloc(1, sizeof(struct lys_type));
|
||
|
LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
(*type)->base = LY_TYPE_ENUM;
|
||
|
(*type)->der = ly_types[LY_TYPE_ENUM];
|
||
|
(*type)->parent = (struct lys_tpdf *)op;
|
||
|
(*type)->info.enums.count = 2;
|
||
|
(*type)->info.enums.enm = calloc(2, sizeof *(*type)->info.enums.enm);
|
||
|
LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
(*type)->info.enums.enm[0].value = 0;
|
||
|
(*type)->info.enums.enm[0].name = lydict_insert(ctx, "subtree", 7);
|
||
|
(*type)->info.enums.enm[1].value = 1;
|
||
|
(*type)->info.enums.enm[1].name = lydict_insert(ctx, "xpath", 5);
|
||
|
for (i = mod->features_size; i > 0; i--) {
|
||
|
if (!strcmp(mod->features[i - 1].name, "xpath")) {
|
||
|
(*type)->info.enums.enm[1].iffeature_size = 1;
|
||
|
(*type)->info.enums.enm[1].iffeature = calloc(1, sizeof(struct lys_feature));
|
||
|
LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
(*type)->info.enums.enm[1].iffeature[0].expr = malloc(sizeof(uint8_t));
|
||
|
LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].expr, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
*(*type)->info.enums.enm[1].iffeature[0].expr = 3; /* LYS_IFF_F */
|
||
|
(*type)->info.enums.enm[1].iffeature[0].features = malloc(sizeof(struct lys_feature*));
|
||
|
LY_CHECK_ERR_RETURN(!(*type)->info.enums.enm[1].iffeature[0].features, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
(*type)->info.enums.enm[1].iffeature[0].features[0] = &mod->features[i - 1];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
mod->ext_size++;
|
||
|
|
||
|
/* 3) filter's select */
|
||
|
op = calloc(1, (sizeof(struct lys_ext_instance_complex) - 1) + 5 * sizeof(void*) + sizeof(uint16_t));
|
||
|
LY_CHECK_ERR_RETURN(!op, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
mod->ext[mod->ext_size] = (struct lys_ext_instance *)op;
|
||
|
op->arg_value = lydict_insert(ctx, "select", 6);
|
||
|
op->def = &ctx->models.list[0]->extensions[0];
|
||
|
op->ext_type = LYEXT_COMPLEX;
|
||
|
op->module = op->parent = mod;
|
||
|
op->parent_type = LYEXT_PAR_MODULE;
|
||
|
op->substmt = ((struct lyext_plugin_complex *)op->def->plugin)->substmt;
|
||
|
op->nodetype = LYS_EXT;
|
||
|
type = (struct lys_type**)&op->content; /* type is stored at offset 0 */
|
||
|
*type = calloc(1, sizeof(struct lys_type));
|
||
|
LY_CHECK_ERR_RETURN(!*type, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
(*type)->base = LY_TYPE_STRING;
|
||
|
(*type)->der = ly_types[LY_TYPE_STRING];
|
||
|
(*type)->parent = (struct lys_tpdf *)op;
|
||
|
mod->ext_size++;
|
||
|
|
||
|
/* 4) URL config */
|
||
|
anyxml = calloc(1, sizeof *anyxml);
|
||
|
LY_CHECK_ERR_RETURN(!anyxml, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
anyxml->nodetype = LYS_ANYXML;
|
||
|
anyxml->prev = (struct lys_node *)anyxml;
|
||
|
anyxml->name = lydict_insert(ctx, "config", 0);
|
||
|
anyxml->module = mod;
|
||
|
anyxml->flags = LYS_CONFIG_W;
|
||
|
if (lys_node_addchild(NULL, mod, (struct lys_node *)anyxml, 0)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* logs directly
|
||
|
* base: 0 - to accept decimal, octal, hexadecimal (in default value)
|
||
|
* 10 - to accept only decimal (instance value)
|
||
|
*/
|
||
|
static int
|
||
|
parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret, struct lyd_node *node)
|
||
|
{
|
||
|
char *strptr;
|
||
|
|
||
|
assert(node);
|
||
|
|
||
|
if (!val_str || !val_str[0]) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* convert to 64-bit integer, all the redundant characters are handled */
|
||
|
errno = 0;
|
||
|
strptr = NULL;
|
||
|
|
||
|
/* parse the value */
|
||
|
*ret = strtoll(val_str, &strptr, base);
|
||
|
if (errno || (*ret < min) || (*ret > max)) {
|
||
|
goto error;
|
||
|
} else if (strptr && *strptr) {
|
||
|
while (isspace(*strptr)) {
|
||
|
++strptr;
|
||
|
}
|
||
|
if (*strptr) {
|
||
|
goto error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
|
||
|
error:
|
||
|
LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* logs directly
|
||
|
* base: 0 - to accept decimal, octal, hexadecimal (in default value)
|
||
|
* 10 - to accept only decimal (instance value)
|
||
|
*/
|
||
|
static int
|
||
|
parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret, struct lyd_node *node)
|
||
|
{
|
||
|
char *strptr;
|
||
|
uint64_t u;
|
||
|
|
||
|
assert(node);
|
||
|
|
||
|
if (!val_str || !val_str[0]) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
errno = 0;
|
||
|
strptr = NULL;
|
||
|
u = strtoull(val_str, &strptr, base);
|
||
|
if (errno || (u > max)) {
|
||
|
goto error;
|
||
|
} else if (strptr && *strptr) {
|
||
|
while (isspace(*strptr)) {
|
||
|
++strptr;
|
||
|
}
|
||
|
if (*strptr) {
|
||
|
goto error;
|
||
|
}
|
||
|
} else if (u != 0 && val_str[0] == '-') {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
*ret = u;
|
||
|
return EXIT_SUCCESS;
|
||
|
|
||
|
error:
|
||
|
LOGVAL(node->schema->module->ctx, LYE_INVAL, LY_VLOG_LYD, node, val_str ? val_str : "", node->schema->name);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* logs directly
|
||
|
*
|
||
|
* kind == 0 - unsigned (unum used), 1 - signed (snum used), 2 - floating point (fnum used)
|
||
|
*/
|
||
|
static int
|
||
|
validate_length_range(uint8_t kind, uint64_t unum, int64_t snum, int64_t fnum, uint8_t fnum_dig, struct lys_type *type,
|
||
|
const char *val_str, struct lyd_node *node)
|
||
|
{
|
||
|
struct lys_restr *restr = NULL;
|
||
|
struct len_ran_intv *intv = NULL, *tmp_intv;
|
||
|
struct lys_type *cur_type;
|
||
|
struct ly_ctx *ctx = type->parent->module->ctx;
|
||
|
int match;
|
||
|
|
||
|
if (resolve_len_ran_interval(ctx, NULL, type, &intv)) {
|
||
|
/* already done during schema parsing */
|
||
|
LOGINT(ctx);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
if (!intv) {
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* I know that all intervals belonging to a single restriction share one type pointer */
|
||
|
tmp_intv = intv;
|
||
|
cur_type = intv->type;
|
||
|
do {
|
||
|
match = 0;
|
||
|
for (; tmp_intv && (tmp_intv->type == cur_type); tmp_intv = tmp_intv->next) {
|
||
|
if (match) {
|
||
|
/* just iterate through the rest of this restriction intervals */
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (((kind == 0) && (unum < tmp_intv->value.uval.min))
|
||
|
|| ((kind == 1) && (snum < tmp_intv->value.sval.min))
|
||
|
|| ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) < 0))) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (((kind == 0) && (unum >= tmp_intv->value.uval.min) && (unum <= tmp_intv->value.uval.max))
|
||
|
|| ((kind == 1) && (snum >= tmp_intv->value.sval.min) && (snum <= tmp_intv->value.sval.max))
|
||
|
|| ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) > -1)
|
||
|
&& (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.max, cur_type->info.dec64.dig) < 1))) {
|
||
|
match = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!match) {
|
||
|
break;
|
||
|
} else if (tmp_intv) {
|
||
|
cur_type = tmp_intv->type;
|
||
|
}
|
||
|
} while (tmp_intv);
|
||
|
|
||
|
while (intv) {
|
||
|
tmp_intv = intv->next;
|
||
|
free(intv);
|
||
|
intv = tmp_intv;
|
||
|
}
|
||
|
|
||
|
if (!match) {
|
||
|
switch (cur_type->base) {
|
||
|
case LY_TYPE_BINARY:
|
||
|
restr = cur_type->info.binary.length;
|
||
|
break;
|
||
|
case LY_TYPE_DEC64:
|
||
|
restr = cur_type->info.dec64.range;
|
||
|
break;
|
||
|
case LY_TYPE_INT8:
|
||
|
case LY_TYPE_INT16:
|
||
|
case LY_TYPE_INT32:
|
||
|
case LY_TYPE_INT64:
|
||
|
case LY_TYPE_UINT8:
|
||
|
case LY_TYPE_UINT16:
|
||
|
case LY_TYPE_UINT32:
|
||
|
case LY_TYPE_UINT64:
|
||
|
restr = cur_type->info.num.range;
|
||
|
break;
|
||
|
case LY_TYPE_STRING:
|
||
|
restr = cur_type->info.str.length;
|
||
|
break;
|
||
|
default:
|
||
|
LOGINT(ctx);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYD, node, (val_str ? val_str : ""), restr ? restr->expr : "");
|
||
|
if (restr && restr->emsg) {
|
||
|
ly_vlog_str(ctx, LY_VLOG_PREV, restr->emsg);
|
||
|
}
|
||
|
if (restr && restr->eapptag) {
|
||
|
ly_err_last_set_apptag(ctx, restr->eapptag);
|
||
|
}
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* logs directly */
|
||
|
static int
|
||
|
validate_pattern(struct ly_ctx *ctx, const char *val_str, struct lys_type *type, struct lyd_node *node)
|
||
|
{
|
||
|
int rc;
|
||
|
unsigned int i;
|
||
|
#ifndef LY_ENABLED_CACHE
|
||
|
pcre *precomp;
|
||
|
#endif
|
||
|
|
||
|
assert(ctx && (type->base == LY_TYPE_STRING));
|
||
|
|
||
|
if (!val_str) {
|
||
|
val_str = "";
|
||
|
}
|
||
|
|
||
|
if (type->der && validate_pattern(ctx, val_str, &type->der->type, node)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
#ifdef LY_ENABLED_CACHE
|
||
|
/* there is no cache, build it */
|
||
|
if (!type->info.str.patterns_pcre && type->info.str.pat_count) {
|
||
|
type->info.str.patterns_pcre = malloc(2 * type->info.str.pat_count * sizeof *type->info.str.patterns_pcre);
|
||
|
LY_CHECK_ERR_RETURN(!type->info.str.patterns_pcre, LOGMEM(ctx), -1);
|
||
|
|
||
|
for (i = 0; i < type->info.str.pat_count; ++i) {
|
||
|
if (lyp_precompile_pattern(ctx, &type->info.str.patterns[i].expr[1],
|
||
|
(pcre**)&type->info.str.patterns_pcre[i * 2],
|
||
|
(pcre_extra**)&type->info.str.patterns_pcre[i * 2 + 1])) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
for (i = 0; i < type->info.str.pat_count; ++i) {
|
||
|
#ifdef LY_ENABLED_CACHE
|
||
|
rc = pcre_exec((pcre *)type->info.str.patterns_pcre[2 * i], (pcre_extra *)type->info.str.patterns_pcre[2 * i + 1],
|
||
|
val_str, strlen(val_str), 0, 0, NULL, 0);
|
||
|
#else
|
||
|
if (lyp_check_pattern(ctx, &type->info.str.patterns[i].expr[1], &precomp)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
rc = pcre_exec(precomp, NULL, val_str, strlen(val_str), 0, 0, NULL, 0);
|
||
|
free(precomp);
|
||
|
#endif
|
||
|
if ((rc && type->info.str.patterns[i].expr[0] == 0x06) || (!rc && type->info.str.patterns[i].expr[0] == 0x15)) {
|
||
|
LOGVAL(ctx, LYE_NOCONSTR, LY_VLOG_LYD, node, val_str, &type->info.str.patterns[i].expr[1]);
|
||
|
if (type->info.str.patterns[i].emsg) {
|
||
|
ly_vlog_str(ctx, LY_VLOG_PREV, type->info.str.patterns[i].emsg);
|
||
|
}
|
||
|
if (type->info.str.patterns[i].eapptag) {
|
||
|
ly_err_last_set_apptag(ctx, type->info.str.patterns[i].eapptag);
|
||
|
}
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
check_number(const char *str_num, const char **num_end, LY_DATA_TYPE base)
|
||
|
{
|
||
|
if (!isdigit(str_num[0]) && (str_num[0] != '-') && (str_num[0] != '+')) {
|
||
|
*num_end = str_num;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((str_num[0] == '-') || (str_num[0] == '+')) {
|
||
|
++str_num;
|
||
|
}
|
||
|
|
||
|
while (isdigit(str_num[0])) {
|
||
|
++str_num;
|
||
|
}
|
||
|
|
||
|
if ((base != LY_TYPE_DEC64) || (str_num[0] != '.') || !isdigit(str_num[1])) {
|
||
|
*num_end = str_num;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
++str_num;
|
||
|
while (isdigit(str_num[0])) {
|
||
|
++str_num;
|
||
|
}
|
||
|
|
||
|
*num_end = str_num;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Checks the syntax of length or range statement,
|
||
|
* on success checks the semantics as well. Does not log.
|
||
|
*
|
||
|
* @param[in] expr Length or range expression.
|
||
|
* @param[in] type Type with the restriction.
|
||
|
*
|
||
|
* @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
|
||
|
*/
|
||
|
int
|
||
|
lyp_check_length_range(struct ly_ctx *ctx, const char *expr, struct lys_type *type)
|
||
|
{
|
||
|
struct len_ran_intv *intv = NULL, *tmp_intv;
|
||
|
const char *c = expr, *tail;
|
||
|
int ret = EXIT_FAILURE, flg = 1; /* first run flag */
|
||
|
|
||
|
assert(expr);
|
||
|
|
||
|
lengthpart:
|
||
|
|
||
|
while (isspace(*c)) {
|
||
|
c++;
|
||
|
}
|
||
|
|
||
|
/* lower boundary or explicit number */
|
||
|
if (!strncmp(c, "max", 3)) {
|
||
|
max:
|
||
|
c += 3;
|
||
|
while (isspace(*c)) {
|
||
|
c++;
|
||
|
}
|
||
|
if (*c != '\0') {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
goto syntax_ok;
|
||
|
|
||
|
} else if (!strncmp(c, "min", 3)) {
|
||
|
if (!flg) {
|
||
|
/* min cannot be used elsewhere than in the first length-part */
|
||
|
goto error;
|
||
|
} else {
|
||
|
flg = 0;
|
||
|
}
|
||
|
c += 3;
|
||
|
while (isspace(*c)) {
|
||
|
c++;
|
||
|
}
|
||
|
|
||
|
if (*c == '|') {
|
||
|
c++;
|
||
|
/* process next length-part */
|
||
|
goto lengthpart;
|
||
|
} else if (*c == '\0') {
|
||
|
goto syntax_ok;
|
||
|
} else if (!strncmp(c, "..", 2)) {
|
||
|
upper:
|
||
|
c += 2;
|
||
|
while (isspace(*c)) {
|
||
|
c++;
|
||
|
}
|
||
|
if (*c == '\0') {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* upper boundary */
|
||
|
if (!strncmp(c, "max", 3)) {
|
||
|
goto max;
|
||
|
}
|
||
|
|
||
|
check_number(c, &tail, type->base);
|
||
|
if (c == tail) {
|
||
|
goto error;
|
||
|
}
|
||
|
c = tail;
|
||
|
while (isspace(*c)) {
|
||
|
c++;
|
||
|
}
|
||
|
if (*c == '\0') {
|
||
|
goto syntax_ok;
|
||
|
} else if (*c == '|') {
|
||
|
c++;
|
||
|
/* process next length-part */
|
||
|
goto lengthpart;
|
||
|
} else {
|
||
|
goto error;
|
||
|
}
|
||
|
} else {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
} else if (isdigit(*c) || (*c == '-') || (*c == '+')) {
|
||
|
/* number */
|
||
|
check_number(c, &tail, type->base);
|
||
|
if (c == tail) {
|
||
|
goto error;
|
||
|
}
|
||
|
c = tail;
|
||
|
|
||
|
while (isspace(*c)) {
|
||
|
c++;
|
||
|
}
|
||
|
|
||
|
if (*c == '|') {
|
||
|
c++;
|
||
|
/* process next length-part */
|
||
|
goto lengthpart;
|
||
|
} else if (*c == '\0') {
|
||
|
goto syntax_ok;
|
||
|
} else if (!strncmp(c, "..", 2)) {
|
||
|
goto upper;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
syntax_ok:
|
||
|
if (resolve_len_ran_interval(ctx, expr, type, &intv)) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
ret = EXIT_SUCCESS;
|
||
|
|
||
|
error:
|
||
|
while (intv) {
|
||
|
tmp_intv = intv->next;
|
||
|
free(intv);
|
||
|
intv = tmp_intv;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Checks pattern syntax. Logs directly.
|
||
|
*
|
||
|
* @param[in] pattern Pattern to check.
|
||
|
* @param[out] pcre_precomp Precompiled PCRE pattern. Can be NULL.
|
||
|
* @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
|
||
|
*/
|
||
|
int
|
||
|
lyp_check_pattern(struct ly_ctx *ctx, const char *pattern, pcre **pcre_precomp)
|
||
|
{
|
||
|
int idx, idx2, start, end, err_offset, count;
|
||
|
char *perl_regex, *ptr;
|
||
|
const char *err_msg, *orig_ptr;
|
||
|
pcre *precomp;
|
||
|
|
||
|
/*
|
||
|
* adjust the expression to a Perl equivalent
|
||
|
*
|
||
|
* http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs
|
||
|
*/
|
||
|
|
||
|
/* we need to replace all "$" with "\$", count them now */
|
||
|
for (count = 0, ptr = strchr(pattern, '$'); ptr; ++count, ptr = strchr(ptr + 1, '$'));
|
||
|
|
||
|
perl_regex = malloc((strlen(pattern) + 4 + count) * sizeof(char));
|
||
|
LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
perl_regex[0] = '\0';
|
||
|
|
||
|
ptr = perl_regex;
|
||
|
|
||
|
if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
|
||
|
/* we wil add line-end anchoring */
|
||
|
ptr[0] = '(';
|
||
|
++ptr;
|
||
|
}
|
||
|
|
||
|
for (orig_ptr = pattern; orig_ptr[0]; ++orig_ptr) {
|
||
|
if (orig_ptr[0] == '$') {
|
||
|
ptr += sprintf(ptr, "\\$");
|
||
|
} else {
|
||
|
ptr[0] = orig_ptr[0];
|
||
|
++ptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
|
||
|
ptr += sprintf(ptr, ")$");
|
||
|
} else {
|
||
|
ptr[0] = '\0';
|
||
|
++ptr;
|
||
|
}
|
||
|
|
||
|
/* substitute Unicode Character Blocks with exact Character Ranges */
|
||
|
while ((ptr = strstr(perl_regex, "\\p{Is"))) {
|
||
|
start = ptr - perl_regex;
|
||
|
|
||
|
ptr = strchr(ptr, '}');
|
||
|
if (!ptr) {
|
||
|
LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 2, "unterminated character property");
|
||
|
free(perl_regex);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
end = (ptr - perl_regex) + 1;
|
||
|
|
||
|
/* need more space */
|
||
|
if (end - start < LYP_URANGE_LEN) {
|
||
|
perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (LYP_URANGE_LEN - (end - start)) + 1);
|
||
|
LY_CHECK_ERR_RETURN(!perl_regex, LOGMEM(ctx); free(perl_regex), EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
/* find our range */
|
||
|
for (idx = 0; lyp_ublock2urange[idx][0]; ++idx) {
|
||
|
if (!strncmp(perl_regex + start + 5, lyp_ublock2urange[idx][0], strlen(lyp_ublock2urange[idx][0]))) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!lyp_ublock2urange[idx][0]) {
|
||
|
LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 5, "unknown block name");
|
||
|
free(perl_regex);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* make the space in the string and replace the block (but we cannot include brackets if it was already enclosed in them) */
|
||
|
for (idx2 = 0, count = 0; idx2 < start; ++idx2) {
|
||
|
if ((perl_regex[idx2] == '[') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) {
|
||
|
++count;
|
||
|
}
|
||
|
if ((perl_regex[idx2] == ']') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) {
|
||
|
--count;
|
||
|
}
|
||
|
}
|
||
|
if (count) {
|
||
|
/* skip brackets */
|
||
|
memmove(perl_regex + start + (LYP_URANGE_LEN - 2), perl_regex + end, strlen(perl_regex + end) + 1);
|
||
|
memcpy(perl_regex + start, lyp_ublock2urange[idx][1] + 1, LYP_URANGE_LEN - 2);
|
||
|
} else {
|
||
|
memmove(perl_regex + start + LYP_URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1);
|
||
|
memcpy(perl_regex + start, lyp_ublock2urange[idx][1], LYP_URANGE_LEN);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* must return 0, already checked during parsing */
|
||
|
precomp = pcre_compile(perl_regex, PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE,
|
||
|
&err_msg, &err_offset, NULL);
|
||
|
if (!precomp) {
|
||
|
LOGVAL(ctx, LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + err_offset, err_msg);
|
||
|
free(perl_regex);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
free(perl_regex);
|
||
|
|
||
|
if (pcre_precomp) {
|
||
|
*pcre_precomp = precomp;
|
||
|
} else {
|
||
|
free(precomp);
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_precompile_pattern(struct ly_ctx *ctx, const char *pattern, pcre** pcre_cmp, pcre_extra **pcre_std)
|
||
|
{
|
||
|
const char *err_msg = NULL;
|
||
|
|
||
|
if (lyp_check_pattern(ctx, pattern, pcre_cmp)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
if (pcre_std && pcre_cmp) {
|
||
|
(*pcre_std) = pcre_study(*pcre_cmp, 0, &err_msg);
|
||
|
if (err_msg) {
|
||
|
LOGWRN(ctx, "Studying pattern \"%s\" failed (%s).", pattern, err_msg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Change the value into its canonical form. In libyang, additionally to the RFC,
|
||
|
* all identities have their module as a prefix in their canonical form.
|
||
|
*
|
||
|
* @param[in] ctx
|
||
|
* @param[in] type Type of the value.
|
||
|
* @param[in,out] value Original and then canonical value.
|
||
|
* @param[in] data1 If \p type is #LY_TYPE_BITS: (struct lys_type_bit **) type bit field,
|
||
|
* #LY_TYPE_DEC64: (int64_t *) parsed digits of the number itself without floating point,
|
||
|
* #LY_TYPE_IDENT: (const char *) local module name (identityref node module),
|
||
|
* #LY_TYPE_INT*: (int64_t *) parsed int number itself,
|
||
|
* #LY_TYPE_UINT*: (uint64_t *) parsed uint number itself,
|
||
|
* otherwise ignored.
|
||
|
* @param[in] data2 If \p type is #LY_TYPE_BITS: (int *) type bit field length,
|
||
|
* #LY_TYPE_DEC64: (uint8_t *) number of fraction digits (position of the floating point),
|
||
|
* otherwise ignored.
|
||
|
* @return 1 if a conversion took place, 0 if the value was kept the same, -1 on error.
|
||
|
*/
|
||
|
static int
|
||
|
make_canonical(struct ly_ctx *ctx, int type, const char **value, void *data1, void *data2)
|
||
|
{
|
||
|
const uint16_t buf_len = 511;
|
||
|
char buf[buf_len + 1];
|
||
|
struct lys_type_bit **bits = NULL;
|
||
|
struct lyxp_expr *exp;
|
||
|
const char *module_name, *cur_expr, *end;
|
||
|
int i, j, count;
|
||
|
int64_t num;
|
||
|
uint64_t unum;
|
||
|
uint8_t c;
|
||
|
|
||
|
#define LOGBUF(str) LOGERR(ctx, LY_EINVAL, "Value \"%s\" is too long.", str)
|
||
|
|
||
|
switch (type) {
|
||
|
case LY_TYPE_BITS:
|
||
|
bits = (struct lys_type_bit **)data1;
|
||
|
count = *((int *)data2);
|
||
|
/* in canonical form, the bits are ordered by their position */
|
||
|
buf[0] = '\0';
|
||
|
for (i = 0; i < count; i++) {
|
||
|
if (!bits[i]) {
|
||
|
/* bit not set */
|
||
|
continue;
|
||
|
}
|
||
|
if (buf[0]) {
|
||
|
LY_CHECK_ERR_RETURN(strlen(buf) + 1 + strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1);
|
||
|
sprintf(buf + strlen(buf), " %s", bits[i]->name);
|
||
|
} else {
|
||
|
LY_CHECK_ERR_RETURN(strlen(bits[i]->name) > buf_len, LOGBUF(bits[i]->name), -1);
|
||
|
strcpy(buf, bits[i]->name);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_IDENT:
|
||
|
module_name = (const char *)data1;
|
||
|
/* identity must always have a prefix */
|
||
|
if (!strchr(*value, ':')) {
|
||
|
sprintf(buf, "%s:%s", module_name, *value);
|
||
|
} else {
|
||
|
strcpy(buf, *value);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_INST:
|
||
|
exp = lyxp_parse_expr(ctx, *value);
|
||
|
LY_CHECK_ERR_RETURN(!exp, LOGINT(ctx), -1);
|
||
|
|
||
|
module_name = NULL;
|
||
|
count = 0;
|
||
|
for (i = 0; (unsigned)i < exp->used; ++i) {
|
||
|
cur_expr = &exp->expr[exp->expr_pos[i]];
|
||
|
|
||
|
/* copy WS */
|
||
|
if (i && ((end = exp->expr + exp->expr_pos[i - 1] + exp->tok_len[i - 1]) != cur_expr)) {
|
||
|
if (count + (cur_expr - end) > buf_len) {
|
||
|
lyxp_expr_free(exp);
|
||
|
LOGBUF(end);
|
||
|
return -1;
|
||
|
}
|
||
|
strncpy(&buf[count], end, cur_expr - end);
|
||
|
count += cur_expr - end;
|
||
|
}
|
||
|
|
||
|
if ((exp->tokens[i] == LYXP_TOKEN_NAMETEST) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) {
|
||
|
/* get the module name with ":" */
|
||
|
++end;
|
||
|
j = end - cur_expr;
|
||
|
|
||
|
if (!module_name || strncmp(cur_expr, module_name, j)) {
|
||
|
/* print module name with colon, it does not equal to the parent one */
|
||
|
if (count + j > buf_len) {
|
||
|
lyxp_expr_free(exp);
|
||
|
LOGBUF(cur_expr);
|
||
|
return -1;
|
||
|
}
|
||
|
strncpy(&buf[count], cur_expr, j);
|
||
|
count += j;
|
||
|
}
|
||
|
module_name = cur_expr;
|
||
|
|
||
|
/* copy the rest */
|
||
|
if (count + (exp->tok_len[i] - j) > buf_len) {
|
||
|
lyxp_expr_free(exp);
|
||
|
LOGBUF(end);
|
||
|
return -1;
|
||
|
}
|
||
|
strncpy(&buf[count], end, exp->tok_len[i] - j);
|
||
|
count += exp->tok_len[i] - j;
|
||
|
} else {
|
||
|
if (count + exp->tok_len[i] > buf_len) {
|
||
|
lyxp_expr_free(exp);
|
||
|
LOGBUF(&exp->expr[exp->expr_pos[i]]);
|
||
|
return -1;
|
||
|
}
|
||
|
strncpy(&buf[count], &exp->expr[exp->expr_pos[i]], exp->tok_len[i]);
|
||
|
count += exp->tok_len[i];
|
||
|
}
|
||
|
}
|
||
|
if (count > buf_len) {
|
||
|
LOGINT(ctx);
|
||
|
lyxp_expr_free(exp);
|
||
|
return -1;
|
||
|
}
|
||
|
buf[count] = '\0';
|
||
|
|
||
|
lyxp_expr_free(exp);
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_DEC64:
|
||
|
num = *((int64_t *)data1);
|
||
|
c = *((uint8_t *)data2);
|
||
|
if (num) {
|
||
|
count = sprintf(buf, "%"PRId64" ", num);
|
||
|
if ( (num > 0 && (count - 1) <= c)
|
||
|
|| (count - 2) <= c ) {
|
||
|
/* we have 0. value, print the value with the leading zeros
|
||
|
* (one for 0. and also keep the correct with of num according
|
||
|
* to fraction-digits value)
|
||
|
* for (num<0) - extra character for '-' sign */
|
||
|
count = sprintf(buf, "%0*"PRId64" ", (num > 0) ? (c + 1) : (c + 2), num);
|
||
|
}
|
||
|
for (i = c, j = 1; i > 0 ; i--) {
|
||
|
if (j && i > 1 && buf[count - 2] == '0') {
|
||
|
/* we have trailing zero to skip */
|
||
|
buf[count - 1] = '\0';
|
||
|
} else {
|
||
|
j = 0;
|
||
|
buf[count - 1] = buf[count - 2];
|
||
|
}
|
||
|
count--;
|
||
|
}
|
||
|
buf[count - 1] = '.';
|
||
|
} else {
|
||
|
/* zero */
|
||
|
sprintf(buf, "0.0");
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_INT8:
|
||
|
case LY_TYPE_INT16:
|
||
|
case LY_TYPE_INT32:
|
||
|
case LY_TYPE_INT64:
|
||
|
num = *((int64_t *)data1);
|
||
|
sprintf(buf, "%"PRId64, num);
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_UINT8:
|
||
|
case LY_TYPE_UINT16:
|
||
|
case LY_TYPE_UINT32:
|
||
|
case LY_TYPE_UINT64:
|
||
|
unum = *((uint64_t *)data1);
|
||
|
sprintf(buf, "%"PRIu64, unum);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
/* should not be even called - just do nothing */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (strcmp(buf, *value)) {
|
||
|
lydict_remove(ctx, *value);
|
||
|
*value = lydict_insert(ctx, buf, 0);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
#undef LOGBUF
|
||
|
}
|
||
|
|
||
|
static const char *
|
||
|
ident_val_add_module_prefix(const char *value, const struct lyxml_elem *xml, struct ly_ctx *ctx)
|
||
|
{
|
||
|
const struct lyxml_ns *ns;
|
||
|
const struct lys_module *mod;
|
||
|
char *str;
|
||
|
|
||
|
do {
|
||
|
LY_TREE_FOR((struct lyxml_ns *)xml->attr, ns) {
|
||
|
if ((ns->type == LYXML_ATTR_NS) && !ns->prefix) {
|
||
|
/* match */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!ns) {
|
||
|
xml = xml->parent;
|
||
|
}
|
||
|
} while (!ns && xml);
|
||
|
|
||
|
if (!ns) {
|
||
|
/* no default namespace */
|
||
|
LOGINT(ctx);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* find module */
|
||
|
mod = ly_ctx_get_module_by_ns(ctx, ns->value, NULL, 1);
|
||
|
if (!mod) {
|
||
|
LOGINT(ctx);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (asprintf(&str, "%s:%s", mod->name, value) == -1) {
|
||
|
LOGMEM(ctx);
|
||
|
return NULL;
|
||
|
}
|
||
|
lydict_remove(ctx, value);
|
||
|
|
||
|
return lydict_insert_zc(ctx, str);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* xml - optional for converting instance-identifier and identityref into JSON format
|
||
|
* leaf - mandatory to know the context (necessary e.g. for prefixes in idenitytref values)
|
||
|
* attr - alternative to leaf in case of parsing value in annotations (attributes)
|
||
|
* local_mod - optional if the local module dos not match the module of leaf/attr
|
||
|
* store - flag for union resolution - we do not want to store the result, we are just learning the type
|
||
|
* dflt - whether the value is a default value from the schema
|
||
|
* trusted - whether the value is trusted to be valid (but may not be canonical, so it is canonized)
|
||
|
*/
|
||
|
struct lys_type *
|
||
|
lyp_parse_value(struct lys_type *type, const char **value_, struct lyxml_elem *xml,
|
||
|
struct lyd_node_leaf_list *leaf, struct lyd_attr *attr, struct lys_module *local_mod,
|
||
|
int store, int dflt, int trusted)
|
||
|
{
|
||
|
struct lys_type *ret = NULL, *t;
|
||
|
struct lys_tpdf *tpdf;
|
||
|
enum int_log_opts prev_ilo;
|
||
|
int c, len, found = 0;
|
||
|
unsigned int i, j;
|
||
|
int64_t num;
|
||
|
uint64_t unum, uind, u = 0;
|
||
|
const char *ptr, *value = *value_, *itemname, *old_val_str = NULL;
|
||
|
struct lys_type_bit **bits = NULL;
|
||
|
struct lys_ident *ident;
|
||
|
lyd_val *val, old_val;
|
||
|
LY_DATA_TYPE *val_type, old_val_type;
|
||
|
uint8_t *val_flags, old_val_flags;
|
||
|
struct lyd_node *contextnode;
|
||
|
struct ly_ctx *ctx = type->parent->module->ctx;
|
||
|
|
||
|
assert(leaf || attr);
|
||
|
|
||
|
if (leaf) {
|
||
|
assert(!attr);
|
||
|
if (!local_mod) {
|
||
|
local_mod = leaf->schema->module;
|
||
|
}
|
||
|
val = &leaf->value;
|
||
|
val_type = &leaf->value_type;
|
||
|
val_flags = &leaf->value_flags;
|
||
|
contextnode = (struct lyd_node *)leaf;
|
||
|
itemname = leaf->schema->name;
|
||
|
} else {
|
||
|
assert(!leaf);
|
||
|
if (!local_mod) {
|
||
|
local_mod = attr->annotation->module;
|
||
|
}
|
||
|
val = &attr->value;
|
||
|
val_type = &attr->value_type;
|
||
|
val_flags = &attr->value_flags;
|
||
|
contextnode = attr->parent;
|
||
|
itemname = attr->name;
|
||
|
}
|
||
|
|
||
|
/* fully clear the value */
|
||
|
if (store) {
|
||
|
old_val_str = lydict_insert(ctx, *value_, 0);
|
||
|
lyd_free_value(*val, *val_type, *val_flags, type, old_val_str, &old_val, &old_val_type, &old_val_flags);
|
||
|
*val_flags &= ~LY_VALUE_UNRES;
|
||
|
}
|
||
|
|
||
|
switch (type->base) {
|
||
|
case LY_TYPE_BINARY:
|
||
|
/* get number of octets for length validation */
|
||
|
unum = 0;
|
||
|
ptr = NULL;
|
||
|
if (value) {
|
||
|
/* silently skip leading/trailing whitespaces */
|
||
|
for (uind = 0; isspace(value[uind]); ++uind);
|
||
|
ptr = &value[uind];
|
||
|
u = strlen(ptr);
|
||
|
while (u && isspace(ptr[u - 1])) {
|
||
|
--u;
|
||
|
}
|
||
|
unum = u;
|
||
|
for (uind = 0; uind < u; ++uind) {
|
||
|
if (ptr[uind] == '\n') {
|
||
|
unum--;
|
||
|
} else if ((ptr[uind] < '/' && ptr[uind] != '+') ||
|
||
|
(ptr[uind] > '9' && ptr[uind] < 'A') ||
|
||
|
(ptr[uind] > 'Z' && ptr[uind] < 'a') || ptr[uind] > 'z') {
|
||
|
if (ptr[uind] == '=') {
|
||
|
/* padding */
|
||
|
if (uind == u - 2 && ptr[uind + 1] == '=') {
|
||
|
found = 2;
|
||
|
uind++;
|
||
|
} else if (uind == u - 1) {
|
||
|
found = 1;
|
||
|
}
|
||
|
}
|
||
|
if (!found) {
|
||
|
/* error */
|
||
|
LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYD, contextnode, ptr[uind], &ptr[uind]);
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Invalid Base64 character.");
|
||
|
goto error;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (unum & 3) {
|
||
|
/* base64 length must be multiple of 4 chars */
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, value);
|
||
|
}
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Base64 encoded value length must be divisible by 4.");
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* length of the encoded string */
|
||
|
len = ((unum / 4) * 3) - found;
|
||
|
if (!trusted && validate_length_range(0, len, 0, 0, 0, type, value, contextnode)) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (value && (ptr != value || ptr[u] != '\0')) {
|
||
|
/* update the changed value */
|
||
|
ptr = lydict_insert(ctx, ptr, u);
|
||
|
lydict_remove(ctx, *value_);
|
||
|
*value_ = ptr;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->binary = value;
|
||
|
*val_type = LY_TYPE_BINARY;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_BITS:
|
||
|
/* locate bits structure with the bits definitions
|
||
|
* since YANG 1.1 allows restricted bits, it is the first
|
||
|
* bits type with some explicit bit specification */
|
||
|
for (; !type->info.bits.count; type = &type->der->type);
|
||
|
|
||
|
if (value || store) {
|
||
|
/* allocate the array of pointers to bits definition */
|
||
|
bits = calloc(type->info.bits.count, sizeof *bits);
|
||
|
LY_CHECK_ERR_GOTO(!bits, LOGMEM(ctx), error);
|
||
|
}
|
||
|
|
||
|
if (!value) {
|
||
|
/* no bits set */
|
||
|
if (store) {
|
||
|
/* store empty array */
|
||
|
val->bit = bits;
|
||
|
*val_type = LY_TYPE_BITS;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
c = 0;
|
||
|
i = 0;
|
||
|
while (value[c]) {
|
||
|
/* skip leading whitespaces */
|
||
|
while (isspace(value[c])) {
|
||
|
c++;
|
||
|
}
|
||
|
if (!value[c]) {
|
||
|
/* trailing white spaces */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* get the length of the bit identifier */
|
||
|
for (len = 0; value[c] && !isspace(value[c]); c++, len++);
|
||
|
|
||
|
/* go back to the beginning of the identifier */
|
||
|
c = c - len;
|
||
|
|
||
|
/* find bit definition, identifiers appear ordered by their position */
|
||
|
for (found = i = 0; i < type->info.bits.count; i++) {
|
||
|
if (!strncmp(type->info.bits.bit[i].name, &value[c], len) && !type->info.bits.bit[i].name[len]) {
|
||
|
/* we have match, check if the value is enabled ... */
|
||
|
for (j = 0; !trusted && (j < type->info.bits.bit[i].iffeature_size); j++) {
|
||
|
if (!resolve_iffeature(&type->info.bits.bit[i].iffeature[j])) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, value);
|
||
|
}
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL,
|
||
|
"Bit \"%s\" is disabled by its %d. if-feature condition.",
|
||
|
type->info.bits.bit[i].name, j + 1);
|
||
|
free(bits);
|
||
|
goto error;
|
||
|
}
|
||
|
}
|
||
|
/* check that the value was not already set */
|
||
|
if (bits[i]) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, value);
|
||
|
}
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Bit \"%s\" used multiple times.",
|
||
|
type->info.bits.bit[i].name);
|
||
|
free(bits);
|
||
|
goto error;
|
||
|
}
|
||
|
/* ... and then store the pointer */
|
||
|
bits[i] = &type->info.bits.bit[i];
|
||
|
|
||
|
/* stop searching */
|
||
|
found = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!found) {
|
||
|
/* referenced bit value does not exist */
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, value);
|
||
|
}
|
||
|
free(bits);
|
||
|
goto error;
|
||
|
}
|
||
|
c = c + len;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_BITS, value_, bits, &type->info.bits.count) == -1) {
|
||
|
free(bits);
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->bit = bits;
|
||
|
*val_type = LY_TYPE_BITS;
|
||
|
} else {
|
||
|
free(bits);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_BOOL:
|
||
|
if (value && !strcmp(value, "true")) {
|
||
|
if (store) {
|
||
|
val->bln = 1;
|
||
|
}
|
||
|
} else if (!value || strcmp(value, "false")) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, value ? value : "");
|
||
|
}
|
||
|
goto error;
|
||
|
} else {
|
||
|
if (store) {
|
||
|
val->bln = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
*val_type = LY_TYPE_BOOL;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_DEC64:
|
||
|
if (!value || !value[0]) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, "");
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
ptr = value;
|
||
|
if (parse_range_dec64(&ptr, type->info.dec64.dig, &num) || ptr[0]) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, value);
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (!trusted && validate_length_range(2, 0, 0, num, type->info.dec64.dig, type, value, contextnode)) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_DEC64, value_, &num, &type->info.dec64.dig) == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->dec64 = num;
|
||
|
*val_type = LY_TYPE_DEC64;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_EMPTY:
|
||
|
if (value && value[0]) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, value);
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
*val_type = LY_TYPE_EMPTY;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_ENUM:
|
||
|
/* locate enums structure with the enumeration definitions,
|
||
|
* since YANG 1.1 allows restricted enums, it is the first
|
||
|
* enum type with some explicit enum specification */
|
||
|
for (; !type->info.enums.count; type = &type->der->type);
|
||
|
|
||
|
/* find matching enumeration value */
|
||
|
for (i = found = 0; i < type->info.enums.count; i++) {
|
||
|
if (value && !strcmp(value, type->info.enums.enm[i].name)) {
|
||
|
/* we have match, check if the value is enabled ... */
|
||
|
for (j = 0; !trusted && (j < type->info.enums.enm[i].iffeature_size); j++) {
|
||
|
if (!resolve_iffeature(&type->info.enums.enm[i].iffeature[j])) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, value);
|
||
|
}
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Enum \"%s\" is disabled by its %d. if-feature condition.",
|
||
|
value, j + 1);
|
||
|
goto error;
|
||
|
}
|
||
|
}
|
||
|
/* ... and store pointer to the definition */
|
||
|
if (store) {
|
||
|
val->enm = &type->info.enums.enm[i];
|
||
|
*val_type = LY_TYPE_ENUM;
|
||
|
}
|
||
|
found = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!found) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, value ? value : "", itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, value ? value : "");
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_IDENT:
|
||
|
if (!value) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, "");
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (xml) {
|
||
|
ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
|
||
|
/* first, convert value into the json format, silently */
|
||
|
value = transform_xml2json(ctx, value, xml, 0, 0);
|
||
|
ly_ilo_restore(NULL, prev_ilo, NULL, 0);
|
||
|
if (!value) {
|
||
|
/* invalid identityref format */
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, *value_);
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* the value has no prefix (default namespace), but the element's namespace has a prefix, find default namespace */
|
||
|
if (!strchr(value, ':') && xml->ns->prefix) {
|
||
|
value = ident_val_add_module_prefix(value, xml, ctx);
|
||
|
if (!value) {
|
||
|
goto error;
|
||
|
}
|
||
|
}
|
||
|
} else if (dflt) {
|
||
|
ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
|
||
|
/* the value actually uses module's prefixes instead of the module names as in JSON format,
|
||
|
* we have to convert it */
|
||
|
value = transform_schema2json(local_mod, value);
|
||
|
ly_ilo_restore(NULL, prev_ilo, NULL, 0);
|
||
|
if (!value) {
|
||
|
/* invalid identityref format or it was already transformed, so ignore the error here */
|
||
|
value = lydict_insert(ctx, *value_, 0);
|
||
|
}
|
||
|
} else {
|
||
|
value = lydict_insert(ctx, *value_, 0);
|
||
|
}
|
||
|
/* value is now in the dictionary, whether it differs from *value_ or not */
|
||
|
|
||
|
ident = resolve_identref(type, value, contextnode, local_mod, dflt);
|
||
|
if (!ident) {
|
||
|
lydict_remove(ctx, value);
|
||
|
goto error;
|
||
|
} else if (store) {
|
||
|
/* store the result */
|
||
|
val->ident = ident;
|
||
|
*val_type = LY_TYPE_IDENT;
|
||
|
}
|
||
|
|
||
|
/* the value is always changed and includes prefix */
|
||
|
if (dflt) {
|
||
|
type->parent->flags |= LYS_DFLTJSON;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_IDENT, &value, (void*)lys_main_module(local_mod)->name, NULL) == -1) {
|
||
|
lydict_remove(ctx, value);
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* replace the old value with the new one (even if they may be the same) */
|
||
|
lydict_remove(ctx, *value_);
|
||
|
*value_ = value;
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_INST:
|
||
|
if (!value) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, "");
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (xml) {
|
||
|
ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
|
||
|
/* first, convert value into the json format, silently */
|
||
|
value = transform_xml2json(ctx, value, xml, 1, 1);
|
||
|
ly_ilo_restore(NULL, prev_ilo, NULL, 0);
|
||
|
if (!value) {
|
||
|
/* invalid instance-identifier format */
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, *value_);
|
||
|
}
|
||
|
goto error;
|
||
|
} else if (ly_strequal(value, *value_, 1)) {
|
||
|
/* we have actually created the same expression (prefixes are the same as the module names)
|
||
|
* so we have just increased dictionary's refcount - fix it */
|
||
|
lydict_remove(ctx, value);
|
||
|
}
|
||
|
} else if (dflt) {
|
||
|
/* turn logging off */
|
||
|
ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
|
||
|
|
||
|
/* the value actually uses module's prefixes instead of the module names as in JSON format,
|
||
|
* we have to convert it */
|
||
|
value = transform_schema2json(local_mod, value);
|
||
|
if (!value) {
|
||
|
/* invalid identityref format or it was already transformed, so ignore the error here */
|
||
|
value = *value_;
|
||
|
} else if (ly_strequal(value, *value_, 1)) {
|
||
|
/* we have actually created the same expression (prefixes are the same as the module names)
|
||
|
* so we have just increased dictionary's refcount - fix it */
|
||
|
lydict_remove(ctx, value);
|
||
|
}
|
||
|
/* turn logging back on */
|
||
|
ly_ilo_restore(NULL, prev_ilo, NULL, 0);
|
||
|
} else {
|
||
|
if ((c = make_canonical(ctx, LY_TYPE_INST, &value, NULL, NULL))) {
|
||
|
if (c == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* if a change occurred, value was removed from the dictionary so fix the pointers */
|
||
|
*value_ = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* note that the data node is an unresolved instance-identifier */
|
||
|
val->instance = NULL;
|
||
|
*val_type = LY_TYPE_INST;
|
||
|
*val_flags |= LY_VALUE_UNRES;
|
||
|
}
|
||
|
|
||
|
if (!ly_strequal(value, *value_, 1)) {
|
||
|
/* update the changed value */
|
||
|
lydict_remove(ctx, *value_);
|
||
|
*value_ = value;
|
||
|
|
||
|
/* we have to remember the conversion into JSON format to be able to print it in correct form */
|
||
|
if (dflt) {
|
||
|
type->parent->flags |= LYS_DFLTJSON;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_LEAFREF:
|
||
|
if (!value) {
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, "", itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, "");
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* it is called not only to get the final type, but mainly to update value to canonical or JSON form
|
||
|
* if needed */
|
||
|
t = lyp_parse_value(&type->info.lref.target->type, value_, xml, leaf, attr, NULL, store, dflt, trusted);
|
||
|
value = *value_; /* refresh possibly changed value */
|
||
|
if (!t) {
|
||
|
/* already logged */
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* make the note that the data node is an unresolved leafref (value union was already filled) */
|
||
|
*val_flags |= LY_VALUE_UNRES;
|
||
|
}
|
||
|
|
||
|
type = t;
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_STRING:
|
||
|
if (!trusted && validate_length_range(0, (value ? ly_strlen_utf8(value) : 0), 0, 0, 0, type, value, contextnode)) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (!trusted && validate_pattern(ctx, value, type, contextnode)) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* special handling of ietf-yang-types xpath1.0 */
|
||
|
for (tpdf = type->der;
|
||
|
tpdf->module && (strcmp(tpdf->name, "xpath1.0") || strcmp(tpdf->module->name, "ietf-yang-types"));
|
||
|
tpdf = tpdf->type.der);
|
||
|
if (tpdf->module && xml) {
|
||
|
/* convert value into the json format */
|
||
|
value = transform_xml2json(ctx, value ? value : "", xml, 1, 1);
|
||
|
if (!value) {
|
||
|
/* invalid instance-identifier format */
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_, itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, *value_);
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (!ly_strequal(value, *value_, 1)) {
|
||
|
/* update the changed value */
|
||
|
lydict_remove(ctx, *value_);
|
||
|
*value_ = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->string = value;
|
||
|
*val_type = LY_TYPE_STRING;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_INT8:
|
||
|
if (parse_int(value, __INT64_C(-128), __INT64_C(127), dflt ? 0 : 10, &num, contextnode)
|
||
|
|| (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_INT8, value_, &num, NULL) == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->int8 = (int8_t)num;
|
||
|
*val_type = LY_TYPE_INT8;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_INT16:
|
||
|
if (parse_int(value, __INT64_C(-32768), __INT64_C(32767), dflt ? 0 : 10, &num, contextnode)
|
||
|
|| (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_INT16, value_, &num, NULL) == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->int16 = (int16_t)num;
|
||
|
*val_type = LY_TYPE_INT16;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_INT32:
|
||
|
if (parse_int(value, __INT64_C(-2147483648), __INT64_C(2147483647), dflt ? 0 : 10, &num, contextnode)
|
||
|
|| (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_INT32, value_, &num, NULL) == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->int32 = (int32_t)num;
|
||
|
*val_type = LY_TYPE_INT32;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_INT64:
|
||
|
if (parse_int(value, __INT64_C(-9223372036854775807) - __INT64_C(1), __INT64_C(9223372036854775807),
|
||
|
dflt ? 0 : 10, &num, contextnode)
|
||
|
|| (!trusted && validate_length_range(1, 0, num, 0, 0, type, value, contextnode))) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_INT64, value_, &num, NULL) == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->int64 = num;
|
||
|
*val_type = LY_TYPE_INT64;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_UINT8:
|
||
|
if (parse_uint(value, __UINT64_C(255), dflt ? 0 : 10, &unum, contextnode)
|
||
|
|| (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_UINT8, value_, &unum, NULL) == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->uint8 = (uint8_t)unum;
|
||
|
*val_type = LY_TYPE_UINT8;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_UINT16:
|
||
|
if (parse_uint(value, __UINT64_C(65535), dflt ? 0 : 10, &unum, contextnode)
|
||
|
|| (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_UINT16, value_, &unum, NULL) == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->uint16 = (uint16_t)unum;
|
||
|
*val_type = LY_TYPE_UINT16;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_UINT32:
|
||
|
if (parse_uint(value, __UINT64_C(4294967295), dflt ? 0 : 10, &unum, contextnode)
|
||
|
|| (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_UINT32, value_, &unum, NULL) == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->uint32 = (uint32_t)unum;
|
||
|
*val_type = LY_TYPE_UINT32;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_UINT64:
|
||
|
if (parse_uint(value, __UINT64_C(18446744073709551615), dflt ? 0 : 10, &unum, contextnode)
|
||
|
|| (!trusted && validate_length_range(0, unum, 0, 0, 0, type, value, contextnode))) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (make_canonical(ctx, LY_TYPE_UINT64, value_, &unum, NULL) == -1) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* store the result */
|
||
|
val->uint64 = unum;
|
||
|
*val_type = LY_TYPE_UINT64;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_TYPE_UNION:
|
||
|
if (store) {
|
||
|
/* unresolved union type */
|
||
|
memset(val, 0, sizeof(lyd_val));
|
||
|
*val_type = LY_TYPE_UNION;
|
||
|
}
|
||
|
|
||
|
if (type->info.uni.has_ptr_type) {
|
||
|
/* we are not resolving anything here, only parsing, and in this case we cannot decide
|
||
|
* the type without resolving it -> we return the union type (resolve it with resolve_union()) */
|
||
|
if (xml) {
|
||
|
/* in case it should resolve into a instance-identifier, we can only do the JSON conversion here */
|
||
|
ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
|
||
|
val->string = transform_xml2json(ctx, value, xml, 1, 1);
|
||
|
ly_ilo_restore(NULL, prev_ilo, NULL, 0);
|
||
|
if (!val->string) {
|
||
|
/* invalid instance-identifier format, likely some other type */
|
||
|
val->string = lydict_insert(ctx, value, 0);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
t = NULL;
|
||
|
found = 0;
|
||
|
|
||
|
/* turn logging off, we are going to try to validate the value with all the types in order */
|
||
|
ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
|
||
|
|
||
|
while ((t = lyp_get_next_union_type(type, t, &found))) {
|
||
|
found = 0;
|
||
|
ret = lyp_parse_value(t, value_, xml, leaf, attr, NULL, store, dflt, 0);
|
||
|
if (ret) {
|
||
|
/* we have the result */
|
||
|
type = ret;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (store) {
|
||
|
/* erase possible present and invalid value data */
|
||
|
lyd_free_value(*val, *val_type, *val_flags, t, *value_, NULL, NULL, NULL);
|
||
|
memset(val, 0, sizeof(lyd_val));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* turn logging back on */
|
||
|
ly_ilo_restore(NULL, prev_ilo, NULL, 0);
|
||
|
|
||
|
if (!t) {
|
||
|
/* not found */
|
||
|
if (store) {
|
||
|
*val_type = 0;
|
||
|
}
|
||
|
if (leaf) {
|
||
|
LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, contextnode, *value_ ? *value_ : "", itemname);
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_INMETA, LY_VLOG_LYD, contextnode, "<none>", itemname, *value_);
|
||
|
}
|
||
|
goto error;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
LOGINT(ctx);
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/* search user types in case this value is supposed to be stored in a custom way */
|
||
|
if (store && type->der && type->der->module) {
|
||
|
c = lytype_store(type->der->module, type->der->name, value_, val);
|
||
|
if (c == -1) {
|
||
|
goto error;
|
||
|
} else if (!c) {
|
||
|
*val_flags |= LY_VALUE_USER;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* free backup */
|
||
|
if (store) {
|
||
|
lyd_free_value(old_val, old_val_type, old_val_flags, type, old_val_str, NULL, NULL, NULL);
|
||
|
lydict_remove(ctx, old_val_str);
|
||
|
}
|
||
|
return type;
|
||
|
|
||
|
error:
|
||
|
/* restore the backup */
|
||
|
if (store) {
|
||
|
*val = old_val;
|
||
|
*val_type = old_val_type;
|
||
|
*val_flags = old_val_flags;
|
||
|
lydict_remove(ctx, old_val_str);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* does not log, cannot fail */
|
||
|
struct lys_type *
|
||
|
lyp_get_next_union_type(struct lys_type *type, struct lys_type *prev_type, int *found)
|
||
|
{
|
||
|
unsigned int i;
|
||
|
struct lys_type *ret = NULL;
|
||
|
|
||
|
while (!type->info.uni.count) {
|
||
|
assert(type->der); /* at least the direct union type has to have type specified */
|
||
|
type = &type->der->type;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < type->info.uni.count; ++i) {
|
||
|
if (type->info.uni.types[i].base == LY_TYPE_UNION) {
|
||
|
ret = lyp_get_next_union_type(&type->info.uni.types[i], prev_type, found);
|
||
|
if (ret) {
|
||
|
break;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!prev_type || *found) {
|
||
|
ret = &type->info.uni.types[i];
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (&type->info.uni.types[i] == prev_type) {
|
||
|
*found = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* ret 0 - ret set, ret 1 - ret not set, no log, ret -1 - ret not set, fatal error */
|
||
|
int
|
||
|
lyp_fill_attr(struct ly_ctx *ctx, struct lyd_node *parent, const char *module_ns, const char *module_name,
|
||
|
const char *attr_name, const char *attr_value, struct lyxml_elem *xml, int options, struct lyd_attr **ret)
|
||
|
{
|
||
|
const struct lys_module *mod = NULL;
|
||
|
const struct lys_submodule *submod = NULL;
|
||
|
struct lys_type **type;
|
||
|
struct lyd_attr *dattr;
|
||
|
int pos, i, j, k;
|
||
|
|
||
|
/* first, get module where the annotation should be defined */
|
||
|
if (module_ns) {
|
||
|
mod = (struct lys_module *)ly_ctx_get_module_by_ns(ctx, module_ns, NULL, 0);
|
||
|
} else if (module_name) {
|
||
|
mod = (struct lys_module *)ly_ctx_get_module(ctx, module_name, NULL, 0);
|
||
|
} else {
|
||
|
LOGINT(ctx);
|
||
|
return -1;
|
||
|
}
|
||
|
if (!mod) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* then, find the appropriate annotation definition */
|
||
|
pos = -1;
|
||
|
for (i = 0, j = 0; i < mod->ext_size; i = i + j + 1) {
|
||
|
j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &mod->ext[i], mod->ext_size - i);
|
||
|
if (j == -1) {
|
||
|
break;
|
||
|
}
|
||
|
if (ly_strequal(mod->ext[i + j]->arg_value, attr_name, 0)) {
|
||
|
pos = i + j;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* try submodules */
|
||
|
if (pos == -1) {
|
||
|
for (k = 0; k < mod->inc_size; ++k) {
|
||
|
submod = mod->inc[k].submodule;
|
||
|
for (i = 0, j = 0; i < submod->ext_size; i = i + j + 1) {
|
||
|
j = lys_ext_instance_presence(&ctx->models.list[0]->extensions[0], &submod->ext[i], submod->ext_size - i);
|
||
|
if (j == -1) {
|
||
|
break;
|
||
|
}
|
||
|
if (ly_strequal(submod->ext[i + j]->arg_value, attr_name, 0)) {
|
||
|
pos = i + j;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pos == -1) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* allocate and fill the data attribute structure */
|
||
|
dattr = calloc(1, sizeof *dattr);
|
||
|
LY_CHECK_ERR_RETURN(!dattr, LOGMEM(ctx), -1);
|
||
|
|
||
|
dattr->parent = parent;
|
||
|
dattr->next = NULL;
|
||
|
dattr->annotation = submod ? (struct lys_ext_instance_complex *)submod->ext[pos] :
|
||
|
(struct lys_ext_instance_complex *)mod->ext[pos];
|
||
|
dattr->name = lydict_insert(ctx, attr_name, 0);
|
||
|
dattr->value_str = lydict_insert(ctx, attr_value, 0);
|
||
|
|
||
|
/* the value is here converted to a JSON format if needed in case of LY_TYPE_IDENT and LY_TYPE_INST or to a
|
||
|
* canonical form of the value */
|
||
|
type = lys_ext_complex_get_substmt(LY_STMT_TYPE, dattr->annotation, NULL);
|
||
|
if (!type || !lyp_parse_value(*type, &dattr->value_str, xml, NULL, dattr, NULL, 1, 0, options & LYD_OPT_TRUSTED)) {
|
||
|
lydict_remove(ctx, dattr->name);
|
||
|
lydict_remove(ctx, dattr->value_str);
|
||
|
free(dattr);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
*ret = dattr;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_check_edit_attr(struct ly_ctx *ctx, struct lyd_attr *attr, struct lyd_node *parent, int *editbits)
|
||
|
{
|
||
|
struct lyd_attr *last = NULL;
|
||
|
int bits = 0;
|
||
|
|
||
|
/* 0x01 - insert attribute present
|
||
|
* 0x02 - insert is relative (before or after)
|
||
|
* 0x04 - value attribute present
|
||
|
* 0x08 - key attribute present
|
||
|
* 0x10 - operation attribute present
|
||
|
* 0x20 - operation not allowing insert attribute (delete or remove)
|
||
|
*/
|
||
|
LY_TREE_FOR(attr, attr) {
|
||
|
last = NULL;
|
||
|
if (!strcmp(attr->annotation->arg_value, "operation") &&
|
||
|
!strcmp(attr->annotation->module->name, "ietf-netconf")) {
|
||
|
if (bits & 0x10) {
|
||
|
LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "operation attributes", parent->schema->name);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
bits |= 0x10;
|
||
|
if (attr->value.enm->value >= 3) {
|
||
|
/* delete or remove */
|
||
|
bits |= 0x20;
|
||
|
}
|
||
|
} else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */
|
||
|
!strcmp(attr->annotation->arg_value, "insert")) {
|
||
|
/* 'insert' attribute present */
|
||
|
if (!(parent->schema->flags & LYS_USERORDERED)) {
|
||
|
/* ... but it is not expected */
|
||
|
LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert");
|
||
|
return -1;
|
||
|
}
|
||
|
if (bits & 0x01) {
|
||
|
LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "insert attributes", parent->schema->name);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
bits |= 0x01;
|
||
|
if (attr->value.enm->value >= 2) {
|
||
|
/* before or after */
|
||
|
bits |= 0x02;
|
||
|
}
|
||
|
last = attr;
|
||
|
} else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */
|
||
|
!strcmp(attr->annotation->arg_value, "value")) {
|
||
|
if (bits & 0x04) {
|
||
|
LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "value attributes", parent->schema->name);
|
||
|
return -1;
|
||
|
} else if (parent->schema->nodetype & LYS_LIST) {
|
||
|
LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name);
|
||
|
return -1;
|
||
|
}
|
||
|
bits |= 0x04;
|
||
|
last = attr;
|
||
|
} else if (attr->annotation->module == ctx->models.list[1] && /* internal YANG schema */
|
||
|
!strcmp(attr->annotation->arg_value, "key")) {
|
||
|
if (bits & 0x08) {
|
||
|
LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, parent, "key attributes", parent->schema->name);
|
||
|
return -1;
|
||
|
} else if (parent->schema->nodetype & LYS_LEAFLIST) {
|
||
|
LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, attr->name);
|
||
|
return -1;
|
||
|
}
|
||
|
bits |= 0x08;
|
||
|
last = attr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* report errors */
|
||
|
if (last && (!(parent->schema->nodetype & (LYS_LEAFLIST | LYS_LIST)) || !(parent->schema->flags & LYS_USERORDERED))) {
|
||
|
/* moving attributes in wrong elements (not an user ordered list or not a list at all) */
|
||
|
LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, last->name);
|
||
|
return -1;
|
||
|
} else if (bits == 3) {
|
||
|
/* 0x01 | 0x02 - relative position, but value/key is missing */
|
||
|
if (parent->schema->nodetype & LYS_LIST) {
|
||
|
LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "key", parent->schema->name);
|
||
|
} else { /* LYS_LEAFLIST */
|
||
|
LOGVAL(ctx, LYE_MISSATTR, LY_VLOG_LYD, parent, "value", parent->schema->name);
|
||
|
}
|
||
|
return -1;
|
||
|
} else if ((bits & (0x04 | 0x08)) && !(bits & 0x02)) {
|
||
|
/* key/value without relative position */
|
||
|
LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, (bits & 0x04) ? "value" : "key");
|
||
|
return -1;
|
||
|
} else if ((bits & 0x21) == 0x21) {
|
||
|
/* insert in delete/remove */
|
||
|
LOGVAL(ctx, LYE_INATTR, LY_VLOG_LYD, parent, "insert");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (editbits) {
|
||
|
*editbits = bits;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* does not log */
|
||
|
static int
|
||
|
dup_identity_check(const char *id, struct lys_ident *ident, uint32_t size)
|
||
|
{
|
||
|
uint32_t i;
|
||
|
|
||
|
for (i = 0; i < size; i++) {
|
||
|
if (ly_strequal(id, ident[i].name, 1)) {
|
||
|
/* name collision */
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
dup_identities_check(const char *id, struct lys_module *module)
|
||
|
{
|
||
|
struct lys_module *mainmod;
|
||
|
int i;
|
||
|
|
||
|
if (dup_identity_check(id, module->ident, module->ident_size)) {
|
||
|
LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* check identity in submodules */
|
||
|
mainmod = lys_main_module(module);
|
||
|
for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; ++i) {
|
||
|
if (dup_identity_check(id, mainmod->inc[i].submodule->ident, mainmod->inc[i].submodule->ident_size)) {
|
||
|
LOGVAL(module->ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "identity", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* does not log */
|
||
|
int
|
||
|
dup_typedef_check(const char *type, struct lys_tpdf *tpdf, int size)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < size; i++) {
|
||
|
if (!strcmp(type, tpdf[i].name)) {
|
||
|
/* name collision */
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* does not log */
|
||
|
static int
|
||
|
dup_feature_check(const char *id, struct lys_module *module)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < module->features_size; i++) {
|
||
|
if (!strcmp(id, module->features[i].name)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* does not log */
|
||
|
static int
|
||
|
dup_prefix_check(const char *prefix, struct lys_module *module)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (module->prefix && !strcmp(module->prefix, prefix)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
for (i = 0; i < module->imp_size; i++) {
|
||
|
if (!strcmp(module->imp[i].prefix, prefix)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* logs directly */
|
||
|
int
|
||
|
lyp_check_identifier(struct ly_ctx *ctx, const char *id, enum LY_IDENT type, struct lys_module *module,
|
||
|
struct lys_node *parent)
|
||
|
{
|
||
|
int i, j;
|
||
|
int size;
|
||
|
struct lys_tpdf *tpdf;
|
||
|
struct lys_node *node;
|
||
|
struct lys_module *mainmod;
|
||
|
struct lys_submodule *submod;
|
||
|
|
||
|
assert(ctx && id);
|
||
|
|
||
|
/* check id syntax */
|
||
|
if (!(id[0] >= 'A' && id[0] <= 'Z') && !(id[0] >= 'a' && id[0] <= 'z') && id[0] != '_') {
|
||
|
LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid start character");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
for (i = 1; id[i]; i++) {
|
||
|
if (!(id[i] >= 'A' && id[i] <= 'Z') && !(id[i] >= 'a' && id[i] <= 'z')
|
||
|
&& !(id[i] >= '0' && id[i] <= '9') && id[i] != '_' && id[i] != '-' && id[i] != '.') {
|
||
|
LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "invalid character");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i > 64) {
|
||
|
LOGWRN(ctx, "Identifier \"%s\" is long, you should use something shorter.", id);
|
||
|
}
|
||
|
|
||
|
switch (type) {
|
||
|
case LY_IDENT_NAME:
|
||
|
/* check uniqueness of the node within its siblings */
|
||
|
if (!parent) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
LY_TREE_FOR(parent->child, node) {
|
||
|
if (ly_strequal(node->name, id, 1)) {
|
||
|
LOGVAL(ctx, LYE_INID, LY_VLOG_NONE, NULL, id, "name duplication");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case LY_IDENT_TYPE:
|
||
|
assert(module);
|
||
|
mainmod = lys_main_module(module);
|
||
|
|
||
|
/* check collision with the built-in types */
|
||
|
if (!strcmp(id, "binary") || !strcmp(id, "bits") ||
|
||
|
!strcmp(id, "boolean") || !strcmp(id, "decimal64") ||
|
||
|
!strcmp(id, "empty") || !strcmp(id, "enumeration") ||
|
||
|
!strcmp(id, "identityref") || !strcmp(id, "instance-identifier") ||
|
||
|
!strcmp(id, "int8") || !strcmp(id, "int16") ||
|
||
|
!strcmp(id, "int32") || !strcmp(id, "int64") ||
|
||
|
!strcmp(id, "leafref") || !strcmp(id, "string") ||
|
||
|
!strcmp(id, "uint8") || !strcmp(id, "uint16") ||
|
||
|
!strcmp(id, "uint32") || !strcmp(id, "uint64") || !strcmp(id, "union")) {
|
||
|
LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, id, "typedef");
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Typedef name duplicates a built-in type.");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* check locally scoped typedefs (avoid name shadowing) */
|
||
|
for (; parent; parent = lys_parent(parent)) {
|
||
|
switch (parent->nodetype) {
|
||
|
case LYS_CONTAINER:
|
||
|
size = ((struct lys_node_container *)parent)->tpdf_size;
|
||
|
tpdf = ((struct lys_node_container *)parent)->tpdf;
|
||
|
break;
|
||
|
case LYS_LIST:
|
||
|
size = ((struct lys_node_list *)parent)->tpdf_size;
|
||
|
tpdf = ((struct lys_node_list *)parent)->tpdf;
|
||
|
break;
|
||
|
case LYS_GROUPING:
|
||
|
size = ((struct lys_node_grp *)parent)->tpdf_size;
|
||
|
tpdf = ((struct lys_node_grp *)parent)->tpdf;
|
||
|
break;
|
||
|
default:
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (dup_typedef_check(id, tpdf, size)) {
|
||
|
LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* check top-level names */
|
||
|
if (dup_typedef_check(id, module->tpdf, module->tpdf_size)) {
|
||
|
LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* check submodule's top-level names */
|
||
|
for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) {
|
||
|
if (dup_typedef_check(id, mainmod->inc[i].submodule->tpdf, mainmod->inc[i].submodule->tpdf_size)) {
|
||
|
LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "typedef", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
case LY_IDENT_PREFIX:
|
||
|
assert(module);
|
||
|
|
||
|
/* check the module itself */
|
||
|
if (dup_prefix_check(id, module)) {
|
||
|
LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "prefix", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
break;
|
||
|
case LY_IDENT_FEATURE:
|
||
|
assert(module);
|
||
|
mainmod = lys_main_module(module);
|
||
|
|
||
|
/* check feature name uniqueness*/
|
||
|
/* check features in the current module */
|
||
|
if (dup_feature_check(id, module)) {
|
||
|
LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* and all its submodules */
|
||
|
for (i = 0; i < mainmod->inc_size && mainmod->inc[i].submodule; i++) {
|
||
|
if (dup_feature_check(id, (struct lys_module *)mainmod->inc[i].submodule)) {
|
||
|
LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "feature", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case LY_IDENT_EXTENSION:
|
||
|
assert(module);
|
||
|
mainmod = lys_main_module(module);
|
||
|
|
||
|
/* check extension name uniqueness in the main module ... */
|
||
|
for (i = 0; i < mainmod->extensions_size; i++) {
|
||
|
if (ly_strequal(id, mainmod->extensions[i].name, 1)) {
|
||
|
LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* ... and all its submodules */
|
||
|
for (j = 0; j < mainmod->inc_size && mainmod->inc[j].submodule; j++) {
|
||
|
submod = mainmod->inc[j].submodule; /* shortcut */
|
||
|
for (i = 0; i < submod->extensions_size; i++) {
|
||
|
if (ly_strequal(id, submod->extensions[i].name, 1)) {
|
||
|
LOGVAL(ctx, LYE_DUPID, LY_VLOG_NONE, NULL, "extension", id);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
/* no check required */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* logs directly */
|
||
|
int
|
||
|
lyp_check_date(struct ly_ctx *ctx, const char *date)
|
||
|
{
|
||
|
int i;
|
||
|
struct tm tm, tm_;
|
||
|
char *r;
|
||
|
|
||
|
assert(date);
|
||
|
|
||
|
/* check format */
|
||
|
for (i = 0; i < LY_REV_SIZE - 1; i++) {
|
||
|
if (i == 4 || i == 7) {
|
||
|
if (date[i] != '-') {
|
||
|
goto error;
|
||
|
}
|
||
|
} else if (!isdigit(date[i])) {
|
||
|
goto error;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* check content, e.g. 2018-02-31 */
|
||
|
memset(&tm, 0, sizeof tm);
|
||
|
r = strptime(date, "%Y-%m-%d", &tm);
|
||
|
if (!r || r != &date[LY_REV_SIZE - 1]) {
|
||
|
goto error;
|
||
|
}
|
||
|
/* set some arbitrary non-0 value in case DST changes, it could move the day otherwise */
|
||
|
tm.tm_hour = 12;
|
||
|
|
||
|
memcpy(&tm_, &tm, sizeof tm);
|
||
|
mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */
|
||
|
if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */
|
||
|
/* checking days is enough, since other errors
|
||
|
* have been checked by strptime() */
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
|
||
|
error:
|
||
|
LOGVAL(ctx, LYE_INDATE, LY_VLOG_NONE, NULL, date);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return
|
||
|
* NULL - success
|
||
|
* root - not yet resolvable
|
||
|
* other node - mandatory node under the root
|
||
|
*/
|
||
|
static const struct lys_node *
|
||
|
lyp_check_mandatory_(const struct lys_node *root)
|
||
|
{
|
||
|
int mand_flag = 0;
|
||
|
const struct lys_node *iter = NULL;
|
||
|
|
||
|
while ((iter = lys_getnext(iter, root, NULL, LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHUSES | LYS_GETNEXT_INTOUSES
|
||
|
| LYS_GETNEXT_INTONPCONT | LYS_GETNEXT_NOSTATECHECK))) {
|
||
|
if (iter->nodetype == LYS_USES) {
|
||
|
if (!((struct lys_node_uses *)iter)->grp) {
|
||
|
/* not yet resolved uses */
|
||
|
return root;
|
||
|
} else {
|
||
|
/* go into uses */
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
if (iter->nodetype == LYS_CHOICE) {
|
||
|
/* skip it, it was already checked for direct mandatory node in default */
|
||
|
continue;
|
||
|
}
|
||
|
if (iter->nodetype == LYS_LIST) {
|
||
|
if (((struct lys_node_list *)iter)->min) {
|
||
|
mand_flag = 1;
|
||
|
}
|
||
|
} else if (iter->nodetype == LYS_LEAFLIST) {
|
||
|
if (((struct lys_node_leaflist *)iter)->min) {
|
||
|
mand_flag = 1;
|
||
|
}
|
||
|
} else if (iter->flags & LYS_MAND_TRUE) {
|
||
|
mand_flag = 1;
|
||
|
}
|
||
|
|
||
|
if (mand_flag) {
|
||
|
return iter;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* logs directly */
|
||
|
int
|
||
|
lyp_check_mandatory_augment(struct lys_node_augment *aug, const struct lys_node *target)
|
||
|
{
|
||
|
const struct lys_node *node;
|
||
|
|
||
|
if (aug->when || target->nodetype == LYS_CHOICE) {
|
||
|
/* - mandatory nodes in new cases are ok;
|
||
|
* clarification from YANG 1.1 - augmentation can add mandatory nodes when it is
|
||
|
* conditional with a when statement */
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
if ((node = lyp_check_mandatory_((struct lys_node *)aug))) {
|
||
|
if (node != (struct lys_node *)aug) {
|
||
|
LOGVAL(target->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory");
|
||
|
LOGVAL(target->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL,
|
||
|
"Mandatory node \"%s\" appears in augment of \"%s\" without when condition.",
|
||
|
node->name, aug->target_name);
|
||
|
return -1;
|
||
|
}
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief check that a mandatory node is not directly under the default case.
|
||
|
* @param[in] node choice with default node
|
||
|
* @return EXIT_SUCCESS if the constraint is fulfilled, EXIT_FAILURE otherwise
|
||
|
*/
|
||
|
int
|
||
|
lyp_check_mandatory_choice(struct lys_node *node)
|
||
|
{
|
||
|
const struct lys_node *mand, *dflt = ((struct lys_node_choice *)node)->dflt;
|
||
|
|
||
|
if ((mand = lyp_check_mandatory_(dflt))) {
|
||
|
if (mand != dflt) {
|
||
|
LOGVAL(node->module->ctx, LYE_INSTMT, LY_VLOG_NONE, NULL, "mandatory");
|
||
|
LOGVAL(node->module->ctx, LYE_SPEC, LY_VLOG_NONE, NULL,
|
||
|
"Mandatory node \"%s\" is directly under the default case \"%s\" of the \"%s\" choice.",
|
||
|
mand->name, dflt->name, node->name);
|
||
|
return -1;
|
||
|
}
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Check status for invalid combination.
|
||
|
*
|
||
|
* @param[in] flags1 Flags of the referencing node.
|
||
|
* @param[in] mod1 Module of the referencing node,
|
||
|
* @param[in] name1 Schema node name of the referencing node.
|
||
|
* @param[in] flags2 Flags of the referenced node.
|
||
|
* @param[in] mod2 Module of the referenced node,
|
||
|
* @param[in] name2 Schema node name of the referenced node.
|
||
|
* @return EXIT_SUCCES on success, EXIT_FAILURE on invalid reference.
|
||
|
*/
|
||
|
int
|
||
|
lyp_check_status(uint16_t flags1, struct lys_module *mod1, const char *name1,
|
||
|
uint16_t flags2, struct lys_module *mod2, const char *name2,
|
||
|
const struct lys_node *node)
|
||
|
{
|
||
|
uint16_t flg1, flg2;
|
||
|
|
||
|
flg1 = (flags1 & LYS_STATUS_MASK) ? (flags1 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
|
||
|
flg2 = (flags2 & LYS_STATUS_MASK) ? (flags2 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
|
||
|
|
||
|
if ((flg1 < flg2) && (lys_main_module(mod1) == lys_main_module(mod2))) {
|
||
|
LOGVAL(mod1->ctx, LYE_INSTATUS, node ? LY_VLOG_LYS : LY_VLOG_NONE, node,
|
||
|
flg1 == LYS_STATUS_CURR ? "current" : "deprecated", name1, "references",
|
||
|
flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", name2);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lyp_del_includedup(struct lys_module *mod, int free_subs)
|
||
|
{
|
||
|
struct ly_modules_list *models = &mod->ctx->models;
|
||
|
uint8_t i;
|
||
|
|
||
|
assert(mod && !mod->type);
|
||
|
|
||
|
if (models->parsed_submodules_count) {
|
||
|
for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i);
|
||
|
if (models->parsed_submodules[i] == mod) {
|
||
|
if (free_subs) {
|
||
|
for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i) {
|
||
|
lys_sub_module_remove_devs_augs((struct lys_module *)models->parsed_submodules[i]);
|
||
|
lys_submodule_module_data_free((struct lys_submodule *)models->parsed_submodules[i]);
|
||
|
lys_submodule_free((struct lys_submodule *)models->parsed_submodules[i], NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
models->parsed_submodules_count = i;
|
||
|
if (!models->parsed_submodules_count) {
|
||
|
free(models->parsed_submodules);
|
||
|
models->parsed_submodules = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
lyp_add_includedup(struct lys_module *sub_mod, struct lys_submodule *parsed_submod)
|
||
|
{
|
||
|
struct ly_modules_list *models = &sub_mod->ctx->models;
|
||
|
int16_t i;
|
||
|
|
||
|
/* store main module if first include */
|
||
|
if (models->parsed_submodules_count) {
|
||
|
for (i = models->parsed_submodules_count - 1; models->parsed_submodules[i]->type; --i);
|
||
|
} else {
|
||
|
i = -1;
|
||
|
}
|
||
|
if ((i == -1) || (models->parsed_submodules[i] != lys_main_module(sub_mod))) {
|
||
|
++models->parsed_submodules_count;
|
||
|
models->parsed_submodules = ly_realloc(models->parsed_submodules,
|
||
|
models->parsed_submodules_count * sizeof *models->parsed_submodules);
|
||
|
LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), );
|
||
|
models->parsed_submodules[models->parsed_submodules_count - 1] = lys_main_module(sub_mod);
|
||
|
}
|
||
|
|
||
|
/* store parsed submodule */
|
||
|
++models->parsed_submodules_count;
|
||
|
models->parsed_submodules = ly_realloc(models->parsed_submodules,
|
||
|
models->parsed_submodules_count * sizeof *models->parsed_submodules);
|
||
|
LY_CHECK_ERR_RETURN(!models->parsed_submodules, LOGMEM(sub_mod->ctx), );
|
||
|
models->parsed_submodules[models->parsed_submodules_count - 1] = (struct lys_module *)parsed_submod;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* types: 0 - include, 1 - import
|
||
|
*/
|
||
|
static int
|
||
|
lyp_check_circmod(struct lys_module *module, const char *value, int type)
|
||
|
{
|
||
|
LY_ECODE code = type ? LYE_CIRC_IMPORTS : LYE_CIRC_INCLUDES;
|
||
|
struct ly_modules_list *models = &module->ctx->models;
|
||
|
uint8_t i;
|
||
|
|
||
|
/* include/import itself */
|
||
|
if (ly_strequal(module->name, value, 1)) {
|
||
|
LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* currently parsed modules */
|
||
|
for (i = 0; i < models->parsing_sub_modules_count; i++) {
|
||
|
if (ly_strequal(models->parsing_sub_modules[i]->name, value, 1)) {
|
||
|
LOGVAL(module->ctx, code, LY_VLOG_NONE, NULL, value);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_check_circmod_add(struct lys_module *module)
|
||
|
{
|
||
|
struct ly_modules_list *models = &module->ctx->models;
|
||
|
|
||
|
/* storing - enlarge the list of modules being currently parsed */
|
||
|
++models->parsing_sub_modules_count;
|
||
|
models->parsing_sub_modules = ly_realloc(models->parsing_sub_modules,
|
||
|
models->parsing_sub_modules_count * sizeof *models->parsing_sub_modules);
|
||
|
LY_CHECK_ERR_RETURN(!models->parsing_sub_modules, LOGMEM(module->ctx), -1);
|
||
|
models->parsing_sub_modules[models->parsing_sub_modules_count - 1] = module;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lyp_check_circmod_pop(struct ly_ctx *ctx)
|
||
|
{
|
||
|
if (!ctx->models.parsing_sub_modules_count) {
|
||
|
LOGINT(ctx);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* update the list of currently being parsed modules */
|
||
|
ctx->models.parsing_sub_modules_count--;
|
||
|
if (!ctx->models.parsing_sub_modules_count) {
|
||
|
free(ctx->models.parsing_sub_modules);
|
||
|
ctx->models.parsing_sub_modules = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* -1 - error - invalid duplicities)
|
||
|
* 0 - success, no duplicity
|
||
|
* 1 - success, valid duplicity found and stored in *sub
|
||
|
*/
|
||
|
static int
|
||
|
lyp_check_includedup(struct lys_module *mod, const char *name, struct lys_include *inc, struct lys_submodule **sub)
|
||
|
{
|
||
|
struct lys_module **parsed_sub = mod->ctx->models.parsed_submodules;
|
||
|
uint8_t i, parsed_sub_count = mod->ctx->models.parsed_submodules_count;
|
||
|
|
||
|
assert(sub);
|
||
|
|
||
|
for (i = 0; i < mod->inc_size; ++i) {
|
||
|
if (ly_strequal(mod->inc[i].submodule->name, name, 1)) {
|
||
|
/* the same module is already included in the same module - error */
|
||
|
LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include");
|
||
|
LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Submodule \"%s\" included twice in the same module \"%s\".",
|
||
|
name, mod->name);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (parsed_sub_count) {
|
||
|
assert(!parsed_sub[0]->type);
|
||
|
for (i = parsed_sub_count - 1; parsed_sub[i]->type; --i) {
|
||
|
if (ly_strequal(parsed_sub[i]->name, name, 1)) {
|
||
|
/* check revisions, including multiple revisions of a single module is error */
|
||
|
if (inc->rev[0] && (!parsed_sub[i]->rev_size || strcmp(parsed_sub[i]->rev[0].date, inc->rev))) {
|
||
|
/* the already included submodule has
|
||
|
* - no revision, but here we require some
|
||
|
* - different revision than the one required here */
|
||
|
LOGVAL(mod->ctx, LYE_INARG, LY_VLOG_NONE, NULL, name, "include");
|
||
|
LOGVAL(mod->ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Including multiple revisions of submodule \"%s\".", name);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* the same module is already included in some other submodule, return it */
|
||
|
(*sub) = (struct lys_submodule *)parsed_sub[i];
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* no duplicity found */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* returns:
|
||
|
* 0 - inc successfully filled
|
||
|
* -1 - error
|
||
|
*/
|
||
|
int
|
||
|
lyp_check_include(struct lys_module *module, const char *value, struct lys_include *inc, struct unres_schema *unres)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/* check that the submodule was not included yet */
|
||
|
i = lyp_check_includedup(module, value, inc, &inc->submodule);
|
||
|
if (i == -1) {
|
||
|
return -1;
|
||
|
} else if (i == 1) {
|
||
|
return 0;
|
||
|
}
|
||
|
/* submodule is not yet loaded */
|
||
|
|
||
|
/* circular include check */
|
||
|
if (lyp_check_circmod(module, value, 0)) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* try to load the submodule */
|
||
|
inc->submodule = (struct lys_submodule *)ly_ctx_load_sub_module(module->ctx, module, value,
|
||
|
inc->rev[0] ? inc->rev : NULL, 1, unres);
|
||
|
|
||
|
/* check the result */
|
||
|
if (!inc->submodule) {
|
||
|
if (ly_errno != LY_EVALID) {
|
||
|
LOGVAL(module->ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "include");
|
||
|
}
|
||
|
LOGERR(module->ctx, LY_EVALID, "Including \"%s\" module into \"%s\" failed.", value, module->name);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* check the revision */
|
||
|
if (inc->rev[0] && inc->submodule->rev_size && strcmp(inc->rev, inc->submodule->rev[0].date)) {
|
||
|
LOGERR(module->ctx, LY_EVALID, "\"%s\" include of submodule \"%s\" in revision \"%s\" not found.",
|
||
|
module->name, value, inc->rev);
|
||
|
unres_schema_free((struct lys_module *)inc->submodule, &unres, 0);
|
||
|
lys_sub_module_remove_devs_augs((struct lys_module *)inc->submodule);
|
||
|
lys_submodule_module_data_free((struct lys_submodule *)inc->submodule);
|
||
|
lys_submodule_free(inc->submodule, NULL);
|
||
|
inc->submodule = NULL;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* store the submodule as successfully parsed */
|
||
|
lyp_add_includedup(module, inc->submodule);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lyp_check_include_missing_recursive(struct lys_module *main_module, struct lys_submodule *sub)
|
||
|
{
|
||
|
uint8_t i, j;
|
||
|
void *reallocated;
|
||
|
int ret = 0, tmp;
|
||
|
struct ly_ctx *ctx = main_module->ctx;
|
||
|
|
||
|
for (i = 0; i < sub->inc_size; i++) {
|
||
|
/* check that the include is also present in the main module */
|
||
|
for (j = 0; j < main_module->inc_size; j++) {
|
||
|
if (main_module->inc[j].submodule == sub->inc[i].submodule) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (j == main_module->inc_size) {
|
||
|
/* match not found */
|
||
|
if (main_module->version >= LYS_VERSION_1_1) {
|
||
|
LOGVAL(ctx, LYE_MISSSTMT, LY_VLOG_NONE, NULL, "include");
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL,
|
||
|
"The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".",
|
||
|
main_module->name, sub->inc[i].submodule->name, sub->name);
|
||
|
/* now we should return error, but due to the issues with freeing the module, we actually have
|
||
|
* to go through the all includes and, as in case of 1.0, add them into the main module and fail
|
||
|
* at the end when all the includes are in the main module and we can free them */
|
||
|
ret = 1;
|
||
|
} else {
|
||
|
/* not strictly an error in YANG 1.0 */
|
||
|
LOGWRN(ctx, "The main module \"%s\" misses include of the \"%s\" submodule used in another submodule \"%s\".",
|
||
|
main_module->name, sub->inc[i].submodule->name, sub->name);
|
||
|
LOGWRN(ctx, "To avoid further issues, adding submodule \"%s\" into the main module \"%s\".",
|
||
|
sub->inc[i].submodule->name, main_module->name);
|
||
|
/* but since it is a good practise and because we expect all the includes in the main module
|
||
|
* when searching it and also when freeing the module, put it into it */
|
||
|
}
|
||
|
main_module->inc_size++;
|
||
|
reallocated = realloc(main_module->inc, main_module->inc_size * sizeof *main_module->inc);
|
||
|
LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx), 1);
|
||
|
main_module->inc = reallocated;
|
||
|
memset(&main_module->inc[main_module->inc_size - 1], 0, sizeof *main_module->inc);
|
||
|
/* to avoid unexpected consequences, copy just a link to the submodule and the revision,
|
||
|
* all other substatements of the include are ignored */
|
||
|
memcpy(&main_module->inc[main_module->inc_size - 1].rev, sub->inc[i].rev, LY_REV_SIZE - 1);
|
||
|
main_module->inc[main_module->inc_size - 1].submodule = sub->inc[i].submodule;
|
||
|
}
|
||
|
|
||
|
/* recursion */
|
||
|
tmp = lyp_check_include_missing_recursive(main_module, sub->inc[i].submodule);
|
||
|
if (!ret && tmp) {
|
||
|
ret = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_check_include_missing(struct lys_module *main_module)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
uint8_t i;
|
||
|
|
||
|
/* in YANG 1.1, all the submodules must be in the main module, check it even for
|
||
|
* 1.0 where it will be printed as warning and the include will be added into the main module */
|
||
|
for (i = 0; i < main_module->inc_size; i++) {
|
||
|
if (lyp_check_include_missing_recursive(main_module, main_module->inc[i].submodule)) {
|
||
|
ret = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/* returns:
|
||
|
* 0 - imp successfully filled
|
||
|
* -1 - error, imp not cleaned
|
||
|
*/
|
||
|
int
|
||
|
lyp_check_import(struct lys_module *module, const char *value, struct lys_import *imp)
|
||
|
{
|
||
|
int i;
|
||
|
struct lys_module *dup = NULL;
|
||
|
struct ly_ctx *ctx = module->ctx;
|
||
|
|
||
|
/* check for importing a single module in multiple revisions */
|
||
|
for (i = 0; i < module->imp_size; i++) {
|
||
|
if (!module->imp[i].module) {
|
||
|
/* skip the not yet filled records */
|
||
|
continue;
|
||
|
}
|
||
|
if (ly_strequal(module->imp[i].module->name, value, 1)) {
|
||
|
/* check revisions, including multiple revisions of a single module is error */
|
||
|
if (imp->rev[0] && (!module->imp[i].module->rev_size || strcmp(module->imp[i].module->rev[0].date, imp->rev))) {
|
||
|
/* the already imported module has
|
||
|
* - no revision, but here we require some
|
||
|
* - different revision than the one required here */
|
||
|
LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import");
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value);
|
||
|
return -1;
|
||
|
} else if (!imp->rev[0]) {
|
||
|
/* no revision, remember the duplication, but check revisions after loading the module
|
||
|
* because the current revision can be the same (then it is ok) or it can differ (then it
|
||
|
* is error */
|
||
|
dup = module->imp[i].module;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* there is duplication, but since prefixes differs (checked in caller of this function),
|
||
|
* it is ok */
|
||
|
imp->module = module->imp[i].module;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* circular import check */
|
||
|
if (lyp_check_circmod(module, value, 1)) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* load module - in specific situations it tries to get the module from the context */
|
||
|
imp->module = (struct lys_module *)ly_ctx_load_sub_module(module->ctx, NULL, value, imp->rev[0] ? imp->rev : NULL,
|
||
|
module->ctx->models.flags & LY_CTX_ALLIMPLEMENTED ? 1 : 0,
|
||
|
NULL);
|
||
|
|
||
|
/* check the result */
|
||
|
if (!imp->module) {
|
||
|
LOGERR(ctx, LY_EVALID, "Importing \"%s\" module into \"%s\" failed.", value, module->name);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (imp->rev[0] && imp->module->rev_size && strcmp(imp->rev, imp->module->rev[0].date)) {
|
||
|
LOGERR(ctx, LY_EVALID, "\"%s\" import of module \"%s\" in revision \"%s\" not found.",
|
||
|
module->name, value, imp->rev);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (dup) {
|
||
|
/* check the revisions */
|
||
|
if ((dup != imp->module) ||
|
||
|
(dup->rev_size != imp->module->rev_size && (!dup->rev_size || imp->module->rev_size)) ||
|
||
|
(dup->rev_size && strcmp(dup->rev[0].date, imp->module->rev[0].date))) {
|
||
|
/* - modules are not the same
|
||
|
* - one of modules has no revision (except they both has no revision)
|
||
|
* - revisions of the modules are not the same */
|
||
|
LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "import");
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Importing multiple revisions of module \"%s\".", value);
|
||
|
return -1;
|
||
|
} else {
|
||
|
LOGWRN(ctx, "Module \"%s\" is imported by \"%s\" multiple times with different prefixes.", dup->name, module->name);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* put the newest revision to the first position
|
||
|
*/
|
||
|
void
|
||
|
lyp_sort_revisions(struct lys_module *module)
|
||
|
{
|
||
|
uint8_t i, r;
|
||
|
struct lys_revision rev;
|
||
|
|
||
|
for (i = 1, r = 0; i < module->rev_size; i++) {
|
||
|
if (strcmp(module->rev[i].date, module->rev[r].date) > 0) {
|
||
|
r = i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (r) {
|
||
|
/* the newest revision is not on position 0, switch them */
|
||
|
memcpy(&rev, &module->rev[0], sizeof rev);
|
||
|
memcpy(&module->rev[0], &module->rev[r], sizeof rev);
|
||
|
memcpy(&module->rev[r], &rev, sizeof rev);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
lyp_ext_instance_rm(struct ly_ctx *ctx, struct lys_ext_instance ***ext, uint8_t *size, uint8_t index)
|
||
|
{
|
||
|
uint8_t i;
|
||
|
|
||
|
lys_extension_instances_free(ctx, (*ext)[index]->ext, (*ext)[index]->ext_size, NULL);
|
||
|
lydict_remove(ctx, (*ext)[index]->arg_value);
|
||
|
free((*ext)[index]);
|
||
|
|
||
|
/* move the rest of the array */
|
||
|
for (i = index + 1; i < (*size); i++) {
|
||
|
(*ext)[i - 1] = (*ext)[i];
|
||
|
}
|
||
|
/* clean the last cell in the array structure */
|
||
|
(*ext)[(*size) - 1] = NULL;
|
||
|
/* the array is not reallocated here, just change its size */
|
||
|
(*size) = (*size) - 1;
|
||
|
|
||
|
if (!(*size)) {
|
||
|
/* ext array is empty */
|
||
|
free((*ext));
|
||
|
ext = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lyp_rfn_apply_ext_(struct lys_refine *rfn, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef)
|
||
|
{
|
||
|
struct ly_ctx *ctx;
|
||
|
int m, n;
|
||
|
struct lys_ext_instance *new;
|
||
|
void *reallocated;
|
||
|
|
||
|
ctx = target->module->ctx; /* shortcut */
|
||
|
|
||
|
m = n = -1;
|
||
|
while ((m = lys_ext_iter(rfn->ext, rfn->ext_size, m + 1, substmt)) != -1) {
|
||
|
/* refine's substatement includes extensions, copy them to the target, replacing the previous
|
||
|
* substatement's extensions if any. In case of refining the extension itself, we are going to
|
||
|
* replace only the same extension (pointing to the same definition) */
|
||
|
if (substmt == LYEXT_SUBSTMT_SELF && rfn->ext[m]->def != extdef) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* get the index of the extension to replace in the target node */
|
||
|
do {
|
||
|
n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt);
|
||
|
} while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef);
|
||
|
|
||
|
/* TODO cover complex extension instances */
|
||
|
if (n == -1) {
|
||
|
/* nothing to replace, we are going to add it - reallocate */
|
||
|
new = malloc(sizeof **target->ext);
|
||
|
LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext);
|
||
|
LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE);
|
||
|
target->ext = reallocated;
|
||
|
target->ext_size++;
|
||
|
|
||
|
/* init */
|
||
|
n = target->ext_size - 1;
|
||
|
target->ext[n] = new;
|
||
|
target->ext[n]->parent = target;
|
||
|
target->ext[n]->parent_type = LYEXT_PAR_NODE;
|
||
|
target->ext[n]->flags = 0;
|
||
|
target->ext[n]->insubstmt = substmt;
|
||
|
target->ext[n]->priv = NULL;
|
||
|
target->ext[n]->nodetype = LYS_EXT;
|
||
|
target->ext[n]->module = target->module;
|
||
|
} else {
|
||
|
/* replacing - first remove the allocated data from target */
|
||
|
lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL);
|
||
|
lydict_remove(ctx, target->ext[n]->arg_value);
|
||
|
}
|
||
|
/* common part for adding and replacing */
|
||
|
target->ext[n]->def = rfn->ext[m]->def;
|
||
|
/* parent and parent_type do not change */
|
||
|
target->ext[n]->arg_value = lydict_insert(ctx, rfn->ext[m]->arg_value, 0);
|
||
|
/* flags do not change */
|
||
|
target->ext[n]->ext_size = rfn->ext[m]->ext_size;
|
||
|
lys_ext_dup(ctx, target->module, rfn->ext[m]->ext, rfn->ext[m]->ext_size, target, LYEXT_PAR_NODE,
|
||
|
&target->ext[n]->ext, 0, NULL);
|
||
|
/* substmt does not change, but the index must be taken from the refine */
|
||
|
target->ext[n]->insubstmt_index = rfn->ext[m]->insubstmt_index;
|
||
|
}
|
||
|
|
||
|
/* remove the rest of extensions belonging to the original substatement in the target node */
|
||
|
while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) {
|
||
|
if (substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef) {
|
||
|
/* keep this extension */
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* remove the item */
|
||
|
lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n);
|
||
|
--n;
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* apply extension instances defined under refine's substatements.
|
||
|
* It cannot be done immediately when applying the refine because there can be
|
||
|
* still unresolved data (e.g. type) and mainly the targeted extension instances.
|
||
|
*/
|
||
|
int
|
||
|
lyp_rfn_apply_ext(struct lys_module *module)
|
||
|
{
|
||
|
int i, k, a = 0;
|
||
|
struct lys_node *root, *nextroot, *next, *node;
|
||
|
struct lys_node *target;
|
||
|
struct lys_node_uses *uses;
|
||
|
struct lys_refine *rfn;
|
||
|
struct ly_set *extset;
|
||
|
|
||
|
/* refines in uses */
|
||
|
LY_TREE_FOR_SAFE(module->data, nextroot, root) {
|
||
|
/* go through the data tree of the module and all the defined augments */
|
||
|
|
||
|
LY_TREE_DFS_BEGIN(root, next, node) {
|
||
|
if (node->nodetype == LYS_USES) {
|
||
|
uses = (struct lys_node_uses *)node;
|
||
|
|
||
|
for (i = 0; i < uses->refine_size; i++) {
|
||
|
if (!uses->refine[i].ext_size) {
|
||
|
/* no extensions in refine */
|
||
|
continue;
|
||
|
}
|
||
|
rfn = &uses->refine[i]; /* shortcut */
|
||
|
|
||
|
/* get the target node */
|
||
|
target = NULL;
|
||
|
resolve_descendant_schema_nodeid(rfn->target_name, uses->child,
|
||
|
LYS_NO_RPC_NOTIF_NODE | LYS_ACTION | LYS_NOTIF,
|
||
|
0, (const struct lys_node **)&target);
|
||
|
if (!target) {
|
||
|
/* it should always succeed since the target_name was already resolved at least
|
||
|
* once when the refine itself was being resolved */
|
||
|
LOGINT(module->ctx);;
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
/* extensions */
|
||
|
extset = ly_set_new();
|
||
|
k = -1;
|
||
|
while ((k = lys_ext_iter(rfn->ext, rfn->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) {
|
||
|
ly_set_add(extset, rfn->ext[k]->def, 0);
|
||
|
}
|
||
|
for (k = 0; (unsigned int)k < extset->number; k++) {
|
||
|
if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) {
|
||
|
ly_set_free(extset);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
ly_set_free(extset);
|
||
|
|
||
|
/* description */
|
||
|
if (rfn->dsc && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DESCRIPTION, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
/* reference */
|
||
|
if (rfn->ref && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_REFERENCE, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
/* config, in case of notification or rpc/action{notif, the config is not applicable
|
||
|
* (there is no config status) */
|
||
|
if ((rfn->flags & LYS_CONFIG_MASK) && (target->flags & LYS_CONFIG_MASK)) {
|
||
|
if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_CONFIG, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
/* default value */
|
||
|
if (rfn->dflt_size && lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_DEFAULT, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
/* mandatory */
|
||
|
if (rfn->flags & LYS_MAND_MASK) {
|
||
|
if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MANDATORY, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
/* presence */
|
||
|
if ((target->nodetype & LYS_CONTAINER) && rfn->mod.presence) {
|
||
|
if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_PRESENCE, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
/* min/max */
|
||
|
if (rfn->flags & LYS_RFN_MINSET) {
|
||
|
if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MIN, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
if (rfn->flags & LYS_RFN_MAXSET) {
|
||
|
if (lyp_rfn_apply_ext_(rfn, target, LYEXT_SUBSTMT_MAX, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
/* must and if-feature contain extensions on their own, not needed to be solved here */
|
||
|
|
||
|
if (target->ext_size) {
|
||
|
/* the allocated target's extension array can be now longer than needed in case
|
||
|
* there is less refine substatement's extensions than in original. Since we are
|
||
|
* going to reduce or keep the same memory, it is not necessary to test realloc's result */
|
||
|
target->ext = realloc(target->ext, target->ext_size * sizeof *target->ext);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
LY_TREE_DFS_END(root, next, node)
|
||
|
}
|
||
|
|
||
|
if (!nextroot && a < module->augment_size) {
|
||
|
nextroot = module->augment[a].child;
|
||
|
a++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* check mandatory substatements defined under extension instances.
|
||
|
*/
|
||
|
int
|
||
|
lyp_mand_check_ext(struct lys_ext_instance_complex *ext, const char *ext_name)
|
||
|
{
|
||
|
void *p;
|
||
|
int i;
|
||
|
struct ly_ctx *ctx = ext->module->ctx;
|
||
|
|
||
|
/* check for mandatory substatements */
|
||
|
for (i = 0; ext->substmt[i].stmt; i++) {
|
||
|
if (ext->substmt[i].cardinality == LY_STMT_CARD_OPT || ext->substmt[i].cardinality == LY_STMT_CARD_ANY) {
|
||
|
/* not a mandatory */
|
||
|
continue;
|
||
|
} else if (ext->substmt[i].cardinality == LY_STMT_CARD_SOME) {
|
||
|
goto array;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* LY_STMT_ORDEREDBY - not checked, has a default value which is the same as explicit system order
|
||
|
* LY_STMT_MODIFIER, LY_STMT_STATUS, LY_STMT_MANDATORY, LY_STMT_CONFIG - checked, but mandatory requirement
|
||
|
* does not make sense since there is also a default value specified
|
||
|
*/
|
||
|
switch(ext->substmt[i].stmt) {
|
||
|
case LY_STMT_ORDEREDBY:
|
||
|
/* always ok */
|
||
|
break;
|
||
|
case LY_STMT_REQINSTANCE:
|
||
|
case LY_STMT_DIGITS:
|
||
|
case LY_STMT_MODIFIER:
|
||
|
p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL);
|
||
|
if (!*(uint8_t*)p) {
|
||
|
LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name);
|
||
|
goto error;
|
||
|
}
|
||
|
break;
|
||
|
case LY_STMT_STATUS:
|
||
|
p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL);
|
||
|
if (!(*(uint16_t*)p & LYS_STATUS_MASK)) {
|
||
|
LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name);
|
||
|
goto error;
|
||
|
}
|
||
|
break;
|
||
|
case LY_STMT_MANDATORY:
|
||
|
p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL);
|
||
|
if (!(*(uint16_t*)p & LYS_MAND_MASK)) {
|
||
|
LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name);
|
||
|
goto error;
|
||
|
}
|
||
|
break;
|
||
|
case LY_STMT_CONFIG:
|
||
|
p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL);
|
||
|
if (!(*(uint16_t*)p & LYS_CONFIG_MASK)) {
|
||
|
LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name);
|
||
|
goto error;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
array:
|
||
|
/* stored as a pointer */
|
||
|
p = lys_ext_complex_get_substmt(ext->substmt[i].stmt, ext, NULL);
|
||
|
if (!(*(void**)p)) {
|
||
|
LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_NONE, NULL, ly_stmt_str[ext->substmt[i].stmt], ext_name);
|
||
|
goto error;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
|
||
|
error:
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lyp_deviate_del_ext(struct lys_node *target, struct lys_ext_instance *ext)
|
||
|
{
|
||
|
int n = -1, found = 0;
|
||
|
char *path;
|
||
|
|
||
|
while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, ext->insubstmt)) != -1) {
|
||
|
if (target->ext[n]->def != ext->def) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (ext->def->argument) {
|
||
|
/* check matching arguments */
|
||
|
if (!ly_strequal(target->ext[n]->arg_value, ext->arg_value, 1)) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* we have the matching extension - remove it */
|
||
|
++found;
|
||
|
lyp_ext_instance_rm(target->module->ctx, &target->ext, &target->ext_size, n);
|
||
|
--n;
|
||
|
}
|
||
|
|
||
|
if (!found) {
|
||
|
path = lys_path(target, LYS_PATH_FIRST_PREFIX);
|
||
|
LOGERR(target->module->ctx, LY_EVALID, "Extension deviation: extension \"%s\" to delete not found in \"%s\".",
|
||
|
ext->def->name, path)
|
||
|
free(path);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
lyp_deviate_apply_ext(struct lys_deviate *dev, struct lys_node *target, LYEXT_SUBSTMT substmt, struct lys_ext *extdef)
|
||
|
{
|
||
|
struct ly_ctx *ctx;
|
||
|
int m, n;
|
||
|
struct lys_ext_instance *new;
|
||
|
void *reallocated;
|
||
|
|
||
|
/* LY_DEVIATE_ADD and LY_DEVIATE_RPL are very similar so they are implement the same way - in replacing,
|
||
|
* there can be some extension instances in the target, in case of adding, there should not be any so we
|
||
|
* will be just adding. */
|
||
|
|
||
|
ctx = target->module->ctx; /* shortcut */
|
||
|
m = n = -1;
|
||
|
|
||
|
while ((m = lys_ext_iter(dev->ext, dev->ext_size, m + 1, substmt)) != -1) {
|
||
|
/* deviate and its substatements include extensions, copy them to the target, replacing the previous
|
||
|
* extensions if any. In case of deviating extension itself, we have to deviate only the same type
|
||
|
* of the extension as specified in the deviation */
|
||
|
if (substmt == LYEXT_SUBSTMT_SELF && dev->ext[m]->def != extdef) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (substmt == LYEXT_SUBSTMT_SELF && dev->mod == LY_DEVIATE_ADD) {
|
||
|
/* in case of adding extension, we will be replacing only the inherited extensions */
|
||
|
do {
|
||
|
n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt);
|
||
|
} while (n != -1 && (target->ext[n]->def != extdef || !(target->ext[n]->flags & LYEXT_OPT_INHERIT)));
|
||
|
} else {
|
||
|
/* get the index of the extension to replace in the target node */
|
||
|
do {
|
||
|
n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt);
|
||
|
/* if we are applying extension deviation, we have to deviate only the same type of the extension */
|
||
|
} while (n != -1 && substmt == LYEXT_SUBSTMT_SELF && target->ext[n]->def != extdef);
|
||
|
}
|
||
|
|
||
|
if (n == -1) {
|
||
|
/* nothing to replace, we are going to add it - reallocate */
|
||
|
new = malloc(sizeof **target->ext);
|
||
|
LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
reallocated = realloc(target->ext, (target->ext_size + 1) * sizeof *target->ext);
|
||
|
LY_CHECK_ERR_RETURN(!reallocated, LOGMEM(ctx); free(new), EXIT_FAILURE);
|
||
|
target->ext = reallocated;
|
||
|
target->ext_size++;
|
||
|
|
||
|
n = target->ext_size - 1;
|
||
|
} else {
|
||
|
/* replacing - the original set of extensions is actually backuped together with the
|
||
|
* node itself, so we are supposed only to free the allocated data here ... */
|
||
|
lys_extension_instances_free(ctx, target->ext[n]->ext, target->ext[n]->ext_size, NULL);
|
||
|
lydict_remove(ctx, target->ext[n]->arg_value);
|
||
|
free(target->ext[n]);
|
||
|
|
||
|
/* and prepare the new structure */
|
||
|
new = malloc(sizeof **target->ext);
|
||
|
LY_CHECK_ERR_RETURN(!new, LOGMEM(ctx), EXIT_FAILURE);
|
||
|
}
|
||
|
/* common part for adding and replacing - fill the newly created / replaced cell */
|
||
|
target->ext[n] = new;
|
||
|
target->ext[n]->def = dev->ext[m]->def;
|
||
|
target->ext[n]->arg_value = lydict_insert(ctx, dev->ext[m]->arg_value, 0);
|
||
|
target->ext[n]->flags = 0;
|
||
|
target->ext[n]->parent = target;
|
||
|
target->ext[n]->parent_type = LYEXT_PAR_NODE;
|
||
|
target->ext[n]->insubstmt = substmt;
|
||
|
target->ext[n]->insubstmt_index = dev->ext[m]->insubstmt_index;
|
||
|
target->ext[n]->ext_size = dev->ext[m]->ext_size;
|
||
|
lys_ext_dup(ctx, target->module, dev->ext[m]->ext, dev->ext[m]->ext_size, target, LYEXT_PAR_NODE,
|
||
|
&target->ext[n]->ext, 1, NULL);
|
||
|
target->ext[n]->nodetype = LYS_EXT;
|
||
|
target->ext[n]->module = target->module;
|
||
|
target->ext[n]->priv = NULL;
|
||
|
|
||
|
/* TODO cover complex extension instances */
|
||
|
}
|
||
|
|
||
|
/* remove the rest of extensions belonging to the original substatement in the target node,
|
||
|
* due to possible reverting of the deviation effect, they are actually not removed, just moved
|
||
|
* to the backup of the original node when the original node is backuped, here we just have to
|
||
|
* free the replaced / deleted originals */
|
||
|
while ((n = lys_ext_iter(target->ext, target->ext_size, n + 1, substmt)) != -1) {
|
||
|
if (substmt == LYEXT_SUBSTMT_SELF) {
|
||
|
/* if we are applying extension deviation, we are going to remove only
|
||
|
* - the same type of the extension in case of replacing
|
||
|
* - the same type of the extension which was inherited in case of adding
|
||
|
* note - delete deviation is covered in lyp_deviate_del_ext */
|
||
|
if (target->ext[n]->def != extdef ||
|
||
|
(dev->mod == LY_DEVIATE_ADD && !(target->ext[n]->flags & LYEXT_OPT_INHERIT))) {
|
||
|
/* keep this extension */
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/* remove the item */
|
||
|
lyp_ext_instance_rm(ctx, &target->ext, &target->ext_size, n);
|
||
|
--n;
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* not-supported deviations are not processed since they affect the complete node, not just their substatements
|
||
|
*/
|
||
|
int
|
||
|
lyp_deviation_apply_ext(struct lys_module *module)
|
||
|
{
|
||
|
int i, j, k;
|
||
|
struct lys_deviate *dev;
|
||
|
struct lys_node *target;
|
||
|
struct ly_set *extset;
|
||
|
|
||
|
for (i = 0; i < module->deviation_size; i++) {
|
||
|
target = NULL;
|
||
|
extset = NULL;
|
||
|
j = resolve_schema_nodeid(module->deviation[i].target_name, NULL, module, &extset, 0, 0);
|
||
|
if (j == -1) {
|
||
|
return EXIT_FAILURE;
|
||
|
} else if (!extset) {
|
||
|
/* LY_DEVIATE_NO */
|
||
|
ly_set_free(extset);
|
||
|
continue;
|
||
|
}
|
||
|
target = extset->set.s[0];
|
||
|
ly_set_free(extset);
|
||
|
|
||
|
for (j = 0; j < module->deviation[i].deviate_size; j++) {
|
||
|
dev = &module->deviation[i].deviate[j];
|
||
|
if (!dev->ext_size) {
|
||
|
/* no extensions in deviate and its substatement, nothing to do here */
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* extensions */
|
||
|
if (dev->mod == LY_DEVIATE_DEL) {
|
||
|
k = -1;
|
||
|
while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) {
|
||
|
if (lyp_deviate_del_ext(target, dev->ext[k])) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* In case of LY_DEVIATE_DEL, we are applying only extension deviation, removing
|
||
|
* of the substatement's extensions was already done when the substatement was applied.
|
||
|
* Extension deviation could not be applied by the parser since the extension could be unresolved,
|
||
|
* which is not the issue of the other substatements. */
|
||
|
continue;
|
||
|
} else {
|
||
|
extset = ly_set_new();
|
||
|
k = -1;
|
||
|
while ((k = lys_ext_iter(dev->ext, dev->ext_size, k + 1, LYEXT_SUBSTMT_SELF)) != -1) {
|
||
|
ly_set_add(extset, dev->ext[k]->def, 0);
|
||
|
}
|
||
|
for (k = 0; (unsigned int)k < extset->number; k++) {
|
||
|
if (lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_SELF, (struct lys_ext *)extset->set.g[k])) {
|
||
|
ly_set_free(extset);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
}
|
||
|
ly_set_free(extset);
|
||
|
}
|
||
|
|
||
|
/* unique */
|
||
|
if (dev->unique_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNIQUE, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
/* units */
|
||
|
if (dev->units && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_UNITS, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
/* default */
|
||
|
if (dev->dflt_size && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_DEFAULT, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
/* config */
|
||
|
if ((dev->flags & LYS_CONFIG_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_CONFIG, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
/* mandatory */
|
||
|
if ((dev->flags & LYS_MAND_MASK) && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MANDATORY, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
/* min/max */
|
||
|
if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MIN, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
if (dev->min_set && lyp_deviate_apply_ext(dev, target, LYEXT_SUBSTMT_MAX, NULL)) {
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
/* type and must contain extension instances in their structures */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_ctx_check_module(struct lys_module *module)
|
||
|
{
|
||
|
struct ly_ctx *ctx;
|
||
|
int i, match_i = -1, to_implement;
|
||
|
const char *last_rev = NULL;
|
||
|
|
||
|
assert(module);
|
||
|
to_implement = 0;
|
||
|
ctx = module->ctx;
|
||
|
|
||
|
/* find latest revision */
|
||
|
for (i = 0; i < module->rev_size; ++i) {
|
||
|
if (!last_rev || (strcmp(last_rev, module->rev[i].date) < 0)) {
|
||
|
last_rev = module->rev[i].date;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < ctx->models.used; i++) {
|
||
|
/* check name (name/revision) and namespace uniqueness */
|
||
|
if (!strcmp(ctx->models.list[i]->name, module->name)) {
|
||
|
if (to_implement) {
|
||
|
if (i == match_i) {
|
||
|
continue;
|
||
|
}
|
||
|
LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.",
|
||
|
module->name, last_rev ? last_rev : "<latest>", ctx->models.list[i]->rev[0].date);
|
||
|
return -1;
|
||
|
} else if (!ctx->models.list[i]->rev_size && module->rev_size) {
|
||
|
LOGERR(ctx, LY_EINVAL, "Module \"%s\" without revision already in context.", module->name);
|
||
|
return -1;
|
||
|
} else if (ctx->models.list[i]->rev_size && !module->rev_size) {
|
||
|
LOGERR(ctx, LY_EINVAL, "Module \"%s\" with revision \"%s\" already in context.",
|
||
|
module->name, ctx->models.list[i]->rev[0].date);
|
||
|
return -1;
|
||
|
} else if ((!module->rev_size && !ctx->models.list[i]->rev_size)
|
||
|
|| !strcmp(ctx->models.list[i]->rev[0].date, last_rev)) {
|
||
|
|
||
|
LOGVRB("Module \"%s@%s\" already in context.", module->name, last_rev ? last_rev : "<latest>");
|
||
|
|
||
|
/* if disabled, enable first */
|
||
|
if (ctx->models.list[i]->disabled) {
|
||
|
lys_set_enabled(ctx->models.list[i]);
|
||
|
}
|
||
|
|
||
|
to_implement = module->implemented;
|
||
|
match_i = i;
|
||
|
if (to_implement && !ctx->models.list[i]->implemented) {
|
||
|
/* check first that it is okay to change it to implemented */
|
||
|
i = -1;
|
||
|
continue;
|
||
|
}
|
||
|
return 1;
|
||
|
|
||
|
} else if (module->implemented && ctx->models.list[i]->implemented) {
|
||
|
LOGERR(ctx, LY_EINVAL, "Module \"%s@%s\" in another revision \"%s\" already implemented.",
|
||
|
module->name, last_rev ? last_rev : "<latest>", ctx->models.list[i]->rev[0].date);
|
||
|
return -1;
|
||
|
}
|
||
|
/* else keep searching, for now the caller is just adding
|
||
|
* another revision of an already present schema
|
||
|
*/
|
||
|
} else if (!strcmp(ctx->models.list[i]->ns, module->ns)) {
|
||
|
LOGERR(ctx, LY_EINVAL, "Two different modules (\"%s\" and \"%s\") have the same namespace \"%s\".",
|
||
|
ctx->models.list[i]->name, module->name, module->ns);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (to_implement) {
|
||
|
if (lys_set_implemented(ctx->models.list[match_i])) {
|
||
|
return -1;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
lyp_ctx_add_module(struct lys_module *module)
|
||
|
{
|
||
|
struct lys_module **newlist = NULL;
|
||
|
int i;
|
||
|
|
||
|
assert(!lyp_ctx_check_module(module));
|
||
|
|
||
|
#ifndef NDEBUG
|
||
|
int j;
|
||
|
/* check that all augments are resolved */
|
||
|
for (i = 0; i < module->augment_size; ++i) {
|
||
|
assert(module->augment[i].target);
|
||
|
}
|
||
|
for (i = 0; i < module->inc_size; ++i) {
|
||
|
for (j = 0; j < module->inc[i].submodule->augment_size; ++j) {
|
||
|
assert(module->inc[i].submodule->augment[j].target);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* add to the context's list of modules */
|
||
|
if (module->ctx->models.used == module->ctx->models.size) {
|
||
|
newlist = realloc(module->ctx->models.list, (2 * module->ctx->models.size) * sizeof *newlist);
|
||
|
LY_CHECK_ERR_RETURN(!newlist, LOGMEM(module->ctx), -1);
|
||
|
for (i = module->ctx->models.size; i < module->ctx->models.size * 2; i++) {
|
||
|
newlist[i] = NULL;
|
||
|
}
|
||
|
module->ctx->models.size *= 2;
|
||
|
module->ctx->models.list = newlist;
|
||
|
}
|
||
|
module->ctx->models.list[module->ctx->models.used++] = module;
|
||
|
module->ctx->models.module_set_id++;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Store UTF-8 character specified as 4byte integer into the dst buffer.
|
||
|
* Returns number of written bytes (4 max), expects that dst has enough space.
|
||
|
*
|
||
|
* UTF-8 mapping:
|
||
|
* 00000000 -- 0000007F: 0xxxxxxx
|
||
|
* 00000080 -- 000007FF: 110xxxxx 10xxxxxx
|
||
|
* 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
|
||
|
* 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||
|
*
|
||
|
* Includes checking for valid characters (following RFC 7950, sec 9.4)
|
||
|
*/
|
||
|
unsigned int
|
||
|
pututf8(struct ly_ctx *ctx, char *dst, int32_t value)
|
||
|
{
|
||
|
if (value < 0x80) {
|
||
|
/* one byte character */
|
||
|
if (value < 0x20 &&
|
||
|
value != 0x09 &&
|
||
|
value != 0x0a &&
|
||
|
value != 0x0d) {
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
dst[0] = value;
|
||
|
return 1;
|
||
|
} else if (value < 0x800) {
|
||
|
/* two bytes character */
|
||
|
dst[0] = 0xc0 | (value >> 6);
|
||
|
dst[1] = 0x80 | (value & 0x3f);
|
||
|
return 2;
|
||
|
} else if (value < 0xfffe) {
|
||
|
/* three bytes character */
|
||
|
if (((value & 0xf800) == 0xd800) ||
|
||
|
(value >= 0xfdd0 && value <= 0xfdef)) {
|
||
|
/* exclude surrogate blocks %xD800-DFFF */
|
||
|
/* exclude noncharacters %xFDD0-FDEF */
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
dst[0] = 0xe0 | (value >> 12);
|
||
|
dst[1] = 0x80 | ((value >> 6) & 0x3f);
|
||
|
dst[2] = 0x80 | (value & 0x3f);
|
||
|
|
||
|
return 3;
|
||
|
} else if (value < 0x10fffe) {
|
||
|
if ((value & 0xffe) == 0xffe) {
|
||
|
/* exclude noncharacters %xFFFE-FFFF, %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
|
||
|
* %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
|
||
|
* %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
|
||
|
goto error;
|
||
|
}
|
||
|
/* four bytes character */
|
||
|
dst[0] = 0xf0 | (value >> 18);
|
||
|
dst[1] = 0x80 | ((value >> 12) & 0x3f);
|
||
|
dst[2] = 0x80 | ((value >> 6) & 0x3f);
|
||
|
dst[3] = 0x80 | (value & 0x3f);
|
||
|
|
||
|
return 4;
|
||
|
}
|
||
|
|
||
|
error:
|
||
|
/* out of range */
|
||
|
LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, NULL);
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
unsigned int
|
||
|
copyutf8(struct ly_ctx *ctx, char *dst, const char *src)
|
||
|
{
|
||
|
uint32_t value;
|
||
|
|
||
|
/* unicode characters */
|
||
|
if (!(src[0] & 0x80)) {
|
||
|
/* one byte character */
|
||
|
if (src[0] < 0x20 &&
|
||
|
src[0] != 0x09 &&
|
||
|
src[0] != 0x0a &&
|
||
|
src[0] != 0x0d) {
|
||
|
LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src);
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%02x", src[0]);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
dst[0] = src[0];
|
||
|
return 1;
|
||
|
} else if (!(src[0] & 0x20)) {
|
||
|
/* two bytes character */
|
||
|
dst[0] = src[0];
|
||
|
dst[1] = src[1];
|
||
|
return 2;
|
||
|
} else if (!(src[0] & 0x10)) {
|
||
|
/* three bytes character */
|
||
|
value = ((uint32_t)(src[0] & 0xf) << 12) | ((uint32_t)(src[1] & 0x3f) << 6) | (src[2] & 0x3f);
|
||
|
if (((value & 0xf800) == 0xd800) ||
|
||
|
(value >= 0xfdd0 && value <= 0xfdef) ||
|
||
|
(value & 0xffe) == 0xffe) {
|
||
|
/* exclude surrogate blocks %xD800-DFFF */
|
||
|
/* exclude noncharacters %xFDD0-FDEF */
|
||
|
/* exclude noncharacters %xFFFE-FFFF */
|
||
|
LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src);
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
dst[0] = src[0];
|
||
|
dst[1] = src[1];
|
||
|
dst[2] = src[2];
|
||
|
return 3;
|
||
|
} else if (!(src[0] & 0x08)) {
|
||
|
/* four bytes character */
|
||
|
value = ((uint32_t)(src[0] & 0x7) << 18) | ((uint32_t)(src[1] & 0x3f) << 12) | ((uint32_t)(src[2] & 0x3f) << 6) | (src[3] & 0x3f);
|
||
|
if ((value & 0xffe) == 0xffe) {
|
||
|
/* exclude noncharacters %x1FFFE-1FFFF, %x2FFFE-2FFFF, %x3FFFE-3FFFF, %x4FFFE-4FFFF,
|
||
|
* %x5FFFE-5FFFF, %x6FFFE-6FFFF, %x7FFFE-7FFFF, %x8FFFE-8FFFF, %x9FFFE-9FFFF, %xAFFFE-AFFFF,
|
||
|
* %xBFFFE-BFFFF, %xCFFFE-CFFFF, %xDFFFE-DFFFF, %xEFFFE-EFFFF, %xFFFFE-FFFFF, %x10FFFE-10FFFF */
|
||
|
LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src);
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 value 0x%08x", value);
|
||
|
return 0;
|
||
|
}
|
||
|
dst[0] = src[0];
|
||
|
dst[1] = src[1];
|
||
|
dst[2] = src[2];
|
||
|
dst[3] = src[3];
|
||
|
return 4;
|
||
|
} else {
|
||
|
LOGVAL(ctx, LYE_XML_INCHAR, LY_VLOG_NONE, NULL, src);
|
||
|
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Invalid UTF-8 leading byte 0x%02x", src[0]);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const struct lys_module *
|
||
|
lyp_get_module(const struct lys_module *module, const char *prefix, int pref_len, const char *name, int name_len, int in_data)
|
||
|
{
|
||
|
const struct lys_module *main_module;
|
||
|
char *str;
|
||
|
int i;
|
||
|
|
||
|
assert(!prefix || !name);
|
||
|
|
||
|
if (prefix && !pref_len) {
|
||
|
pref_len = strlen(prefix);
|
||
|
}
|
||
|
if (name && !name_len) {
|
||
|
name_len = strlen(name);
|
||
|
}
|
||
|
|
||
|
main_module = lys_main_module(module);
|
||
|
|
||
|
/* module own prefix, submodule own prefix, (sub)module own name */
|
||
|
if ((!prefix || (!module->type && !strncmp(main_module->prefix, prefix, pref_len) && !main_module->prefix[pref_len])
|
||
|
|| (module->type && !strncmp(module->prefix, prefix, pref_len) && !module->prefix[pref_len]))
|
||
|
&& (!name || (!strncmp(main_module->name, name, name_len) && !main_module->name[name_len]))) {
|
||
|
return main_module;
|
||
|
}
|
||
|
|
||
|
/* standard import */
|
||
|
for (i = 0; i < module->imp_size; ++i) {
|
||
|
if ((!prefix || (!strncmp(module->imp[i].prefix, prefix, pref_len) && !module->imp[i].prefix[pref_len]))
|
||
|
&& (!name || (!strncmp(module->imp[i].module->name, name, name_len) && !module->imp[i].module->name[name_len]))) {
|
||
|
return module->imp[i].module;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* module required by a foreign grouping, deviation, or submodule */
|
||
|
if (name) {
|
||
|
str = strndup(name, name_len);
|
||
|
if (!str) {
|
||
|
LOGMEM(module->ctx);
|
||
|
return NULL;
|
||
|
}
|
||
|
main_module = ly_ctx_get_module(module->ctx, str, NULL, 0);
|
||
|
|
||
|
/* try data callback */
|
||
|
if (!main_module && in_data && module->ctx->data_clb) {
|
||
|
main_module = module->ctx->data_clb(module->ctx, str, NULL, 0, module->ctx->data_clb_data);
|
||
|
}
|
||
|
|
||
|
free(str);
|
||
|
return main_module;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
const struct lys_module *
|
||
|
lyp_get_import_module_ns(const struct lys_module *module, const char *ns)
|
||
|
{
|
||
|
int i;
|
||
|
const struct lys_module *mod = NULL;
|
||
|
|
||
|
assert(module && ns);
|
||
|
|
||
|
if (module->type) {
|
||
|
/* the module is actually submodule and to get the namespace, we need the main module */
|
||
|
if (ly_strequal(((struct lys_submodule *)module)->belongsto->ns, ns, 0)) {
|
||
|
return ((struct lys_submodule *)module)->belongsto;
|
||
|
}
|
||
|
} else {
|
||
|
/* module's own namespace */
|
||
|
if (ly_strequal(module->ns, ns, 0)) {
|
||
|
return module;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* imported modules */
|
||
|
for (i = 0; i < module->imp_size; ++i) {
|
||
|
if (ly_strequal(module->imp[i].module->ns, ns, 0)) {
|
||
|
return module->imp[i].module;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return mod;
|
||
|
}
|
||
|
|
||
|
const char *
|
||
|
lyp_get_yang_data_template_name(const struct lyd_node *node)
|
||
|
{
|
||
|
struct lys_node *snode;
|
||
|
|
||
|
snode = lys_parent(node->schema);
|
||
|
while (snode && snode->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE)) {
|
||
|
snode = lys_parent(snode);
|
||
|
}
|
||
|
|
||
|
if (snode && snode->nodetype == LYS_EXT && strcmp(((struct lys_ext_instance_complex *)snode)->def->name, "yang-data") == 0) {
|
||
|
return ((struct lys_ext_instance_complex *)snode)->arg_value;
|
||
|
} else {
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const struct lys_node *
|
||
|
lyp_get_yang_data_template(const struct lys_module *module, const char *yang_data_name, int yang_data_name_len)
|
||
|
{
|
||
|
int i, j;
|
||
|
const struct lys_node *ret = NULL;
|
||
|
const struct lys_submodule *submodule;
|
||
|
|
||
|
for(i = 0; i < module->ext_size; ++i) {
|
||
|
if (!strcmp(module->ext[i]->def->name, "yang-data") && !strncmp(module->ext[i]->arg_value, yang_data_name, yang_data_name_len)
|
||
|
&& !module->ext[i]->arg_value[yang_data_name_len]) {
|
||
|
ret = (struct lys_node *)module->ext[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(j = 0; !ret && j < module->inc_size; ++j) {
|
||
|
submodule = module->inc[j].submodule;
|
||
|
for(i = 0; i < submodule->ext_size; ++i) {
|
||
|
if (!strcmp(submodule->ext[i]->def->name, "yang-data") && !strncmp(submodule->ext[i]->arg_value, yang_data_name, yang_data_name_len)
|
||
|
&& !submodule->ext[i]->arg_value[yang_data_name_len]) {
|
||
|
ret = (struct lys_node *)submodule->ext[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|