| /** |
| * @file resolve.c |
| * @author Michal Vasko <mvasko@cesnet.cz> |
| * @brief libyang resolve functions |
| * |
| * Copyright (c) 2015 CESNET, z.s.p.o. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * 3. Neither the name of the Company nor the names of its contributors |
| * may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| */ |
| |
| #define _GNU_SOURCE |
| |
| #include <stdlib.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <limits.h> |
| |
| #include "libyang.h" |
| #include "resolve.h" |
| #include "common.h" |
| #include "xpath.h" |
| #include "dict_private.h" |
| #include "tree_internal.h" |
| |
| /** |
| * @brief Parse an identifier. |
| * |
| * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l')) |
| * identifier = (ALPHA / "_") |
| * *(ALPHA / DIGIT / "_" / "-" / ".") |
| * |
| * @param[in] id Identifier to use. |
| * |
| * @return Number of characters successfully parsed. |
| */ |
| int |
| parse_identifier(const char *id) |
| { |
| int parsed = 0; |
| |
| if (((id[0] == 'x') || (id[0] == 'X')) |
| && ((id[1] == 'm') || (id[0] == 'M')) |
| && ((id[2] == 'l') || (id[2] == 'L'))) { |
| return -parsed; |
| } |
| |
| if (!isalpha(id[0]) && (id[0] != '_')) { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isalnum(id[0]) || (id[0] == '_') || (id[0] == '-') || (id[0] == '.')) { |
| ++parsed; |
| ++id; |
| } |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse a node-identifier. |
| * |
| * node-identifier = [prefix ":"] identifier |
| * |
| * @param[in] id Identifier to use. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| static int |
| parse_node_identifier(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len) |
| { |
| int parsed = 0, ret; |
| |
| assert(id); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| |
| if ((ret = parse_identifier(id)) < 1) { |
| return ret; |
| } |
| |
| if (prefix) { |
| *prefix = id; |
| } |
| if (pref_len) { |
| *pref_len = ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| /* there is prefix */ |
| if (id[0] == ':') { |
| ++parsed; |
| ++id; |
| |
| /* there isn't */ |
| } else { |
| if (name && prefix) { |
| *name = *prefix; |
| } |
| if (prefix) { |
| *prefix = NULL; |
| } |
| |
| if (nam_len && pref_len) { |
| *nam_len = *pref_len; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| |
| return parsed; |
| } |
| |
| /* identifier (node name) */ |
| if ((ret = parse_identifier(id)) < 1) { |
| return -parsed+ret; |
| } |
| |
| if (name) { |
| *name = id; |
| } |
| if (nam_len) { |
| *nam_len = ret; |
| } |
| |
| return parsed+ret; |
| } |
| |
| /** |
| * @brief Parse a path-predicate (leafref). |
| * |
| * path-predicate = "[" *WSP path-equality-expr *WSP "]" |
| * path-equality-expr = node-identifier *WSP "=" *WSP path-key-expr |
| * |
| * @param[in] id Identifier to use. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] path_key_expr Points to the path-key-expr. |
| * @param[out] pke_len Length of the path-key-expr. |
| * @param[out] has_predicate Flag to mark whether there is another predicate following. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| static int |
| parse_path_predicate(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, |
| const char **path_key_expr, int *pke_len, int *has_predicate) |
| { |
| const char *ptr; |
| int parsed = 0, ret; |
| |
| assert(id); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| if (path_key_expr) { |
| *path_key_expr = NULL; |
| } |
| if (pke_len) { |
| *pke_len = 0; |
| } |
| if (has_predicate) { |
| *has_predicate = 0; |
| } |
| |
| if (id[0] != '[') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != '=') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if ((ptr = strchr(id, ']')) == NULL) { |
| return -parsed; |
| } |
| |
| --ptr; |
| while (isspace(ptr[0])) { |
| --ptr; |
| } |
| ++ptr; |
| |
| ret = ptr-id; |
| if (path_key_expr) { |
| *path_key_expr = id; |
| } |
| if (pke_len) { |
| *pke_len = ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| assert(id[0] == ']'); |
| |
| if (id[1] == '[') { |
| *has_predicate = 1; |
| } |
| |
| return parsed+1; |
| } |
| |
| /** |
| * @brief Parse a path-key-expr (leafref). First call parses "current()", all |
| * the ".." and the first node-identifier, other calls parse a single |
| * node-identifier each. |
| * |
| * path-key-expr = current-function-invocation *WSP "/" *WSP |
| * rel-path-keyexpr |
| * rel-path-keyexpr = 1*(".." *WSP "/" *WSP) |
| * *(node-identifier *WSP "/" *WSP) |
| * node-identifier |
| * |
| * @param[in] id Identifier to use. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] parent_times Number of ".." in the path. Must be 0 on the first call, |
| * must not be changed between consecutive calls. |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| static int |
| parse_path_key_expr(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, |
| int *parent_times) |
| { |
| int parsed = 0, ret, par_times = 0; |
| |
| assert(id); |
| assert(parent_times); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| |
| if (!*parent_times) { |
| /* current-function-invocation *WSP "/" *WSP rel-path-keyexpr */ |
| if (strncmp(id, "current()", 9)) { |
| return -parsed; |
| } |
| |
| parsed += 9; |
| id += 9; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| /* rel-path-keyexpr */ |
| if (strncmp(id, "..", 2)) { |
| return -parsed; |
| } |
| ++par_times; |
| |
| parsed += 2; |
| id += 2; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| } |
| |
| /* 1*(".." *WSP "/" *WSP) *(node-identifier *WSP "/" *WSP) node-identifier |
| * |
| * first parent reference with whitespaces already parsed |
| */ |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| while (!strncmp(id, "..", 2) && !*parent_times) { |
| ++par_times; |
| |
| parsed += 2; |
| id += 2; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| } |
| |
| if (!*parent_times) { |
| *parent_times = par_times; |
| } |
| |
| /* all parent references must be parsed at this point */ |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse path-arg (leafref). |
| * |
| * path-arg = absolute-path / relative-path |
| * absolute-path = 1*("/" (node-identifier *path-predicate)) |
| * relative-path = 1*(".." "/") descendant-path |
| * |
| * @param[in] id Identifier to use. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] parent_times Number of ".." in the path. Must be 0 on the first call, |
| * must not be changed between consecutive calls. -1 if the |
| * path is relative. |
| * @param[out] has_predicate Flag to mark whether there is a predicate specified. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| static int |
| parse_path_arg(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, int *parent_times, |
| int *has_predicate) |
| { |
| int parsed = 0, ret, par_times = 0; |
| |
| assert(id); |
| assert(parent_times); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| if (has_predicate) { |
| *has_predicate = 0; |
| } |
| |
| if (!*parent_times && !strncmp(id, "..", 2)) { |
| ++par_times; |
| |
| parsed += 2; |
| id += 2; |
| |
| while (!strncmp(id, "/..", 3)) { |
| ++par_times; |
| |
| parsed += 3; |
| id += 3; |
| } |
| } |
| |
| if (!*parent_times) { |
| if (par_times) { |
| *parent_times = par_times; |
| } else { |
| *parent_times = -1; |
| } |
| } |
| |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| /* skip '/' */ |
| ++parsed; |
| ++id; |
| |
| /* node-identifier ([prefix:]identifier) */ |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed-ret; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| /* there is no predicate */ |
| if ((id[0] == '/') || !id[0]) { |
| return parsed; |
| } else if (id[0] != '[') { |
| return -parsed; |
| } |
| |
| if (has_predicate) { |
| *has_predicate = 1; |
| } |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse instance-identifier in JSON format. That means that prefixes |
| * (which are mandatory) are actually model names. |
| * |
| * instance-identifier = 1*("/" (node-identifier *predicate)) |
| * |
| * @param[in] id Identifier to use. |
| * @param[out] model Points to the model name. |
| * @param[out] mod_len Length of the model name. |
| * @param[out] name Points to the node name. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] has_predicate Flag to mark whether there is a predicate specified. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| static int |
| parse_instance_identifier_json(const char *id, const char **model, int *mod_len, const char **name, int *nam_len, |
| int *has_predicate) |
| { |
| int parsed = 0, ret; |
| |
| assert(id); |
| if (model) { |
| *model = NULL; |
| } |
| if (mod_len) { |
| *mod_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| if (has_predicate) { |
| *has_predicate = 0; |
| } |
| |
| if (id[0] != '/') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| if ((ret = parse_node_identifier(id, model, mod_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } else if (model && !*model) { |
| return -parsed; |
| } |
| |
| parsed += ret; |
| id += ret; |
| |
| if ((id[0] == '[') && has_predicate) { |
| *has_predicate = 1; |
| } |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse predicate (instance-identifier) in JSON format. That means that prefixes |
| * (which are mandatory) are actually model names. |
| * |
| * predicate = "[" *WSP (predicate-expr / pos) *WSP "]" |
| * predicate-expr = (node-identifier / ".") *WSP "=" *WSP |
| * ((DQUOTE string DQUOTE) / |
| * (SQUOTE string SQUOTE)) |
| * pos = non-negative-integer-value |
| * |
| * @param[in] id Identifier to use. |
| * @param[out] model Points to the model name. |
| * @param[out] mod_len Length of the model name. |
| * @param[out] name Points to the node name. Can be identifier (from node-identifier), "." or pos. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] value Value the node-identifier must have (string from the grammar), |
| * NULL if there is not any. |
| * @param[out] val_len Length of the value, 0 if there is not any. |
| * @param[out] has_predicate Flag to mark whether there is a predicate specified. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| static int |
| parse_predicate_json(const char *id, const char **model, int *mod_len, const char **name, int *nam_len, |
| const char **value, int *val_len, int *has_predicate) |
| { |
| const char *ptr; |
| int parsed = 0, ret; |
| char quote; |
| |
| assert(id); |
| if (model) { |
| *model = NULL; |
| } |
| if (mod_len) { |
| *mod_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| if (value) { |
| *value = NULL; |
| } |
| if (val_len) { |
| *val_len = 0; |
| } |
| if (has_predicate) { |
| *has_predicate = 0; |
| } |
| |
| if (id[0] != '[') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| /* pos */ |
| if (isdigit(id[0])) { |
| if (name) { |
| *name = id; |
| } |
| |
| if (id[0] == '0') { |
| ++parsed; |
| ++id; |
| |
| if (isdigit(id[0])) { |
| return -parsed; |
| } |
| } |
| |
| while (isdigit(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (nam_len) { |
| *nam_len = id-(*name); |
| } |
| |
| /* "." */ |
| } else if (id[0] == '.') { |
| if (name) { |
| *name = id; |
| } |
| if (nam_len) { |
| *nam_len = 1; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| /* node-identifier */ |
| } else { |
| if ((ret = parse_node_identifier(id, model, mod_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } else if (model && !*model) { |
| return -parsed; |
| } |
| |
| parsed += ret; |
| id += ret; |
| } |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != '=') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| /* ((DQUOTE string DQUOTE) / (SQUOTE string SQUOTE)) */ |
| if ((id[0] == '\"') || (id[0] == '\'')) { |
| quote = id[0]; |
| |
| ++parsed; |
| ++id; |
| |
| if ((ptr = strchr(id, quote)) == NULL) { |
| return -parsed; |
| } |
| ret = ptr-id; |
| |
| if (value) { |
| *value = id; |
| } |
| if (val_len) { |
| *val_len = ret; |
| } |
| |
| parsed += ret+1; |
| id += ret+1; |
| } else { |
| return -parsed; |
| } |
| |
| while (isspace(id[0])) { |
| ++parsed; |
| ++id; |
| } |
| |
| if (id[0] != ']') { |
| return -parsed; |
| } |
| |
| ++parsed; |
| ++id; |
| |
| if ((id[0] == '[') && has_predicate) { |
| *has_predicate = 1; |
| } |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Parse schema-nodeid. |
| * |
| * schema-nodeid = absolute-schema-nodeid / |
| * descendant-schema-nodeid |
| * absolute-schema-nodeid = 1*("/" node-identifier) |
| * descendant-schema-nodeid = |
| * node-identifier |
| * absolute-schema-nodeid |
| * |
| * @param[in] id Identifier to use. |
| * @param[out] prefix Points to the prefix, NULL if there is not any. |
| * @param[out] pref_len Length of the prefix, 0 if there is not any. |
| * @param[out] name Points to the node name. Can be identifier (from node-identifier), "." or pos. |
| * @param[out] nam_len Length of the node name. |
| * @param[out] is_relative Flag to mark whether the nodeid is absolute or descendant. Must be -1 |
| * on the first call, must not be changed between consecutive calls. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| static int |
| parse_schema_nodeid(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len, |
| int *is_relative) |
| { |
| int parsed = 0, ret; |
| |
| assert(id); |
| assert(is_relative); |
| if (prefix) { |
| *prefix = NULL; |
| } |
| if (pref_len) { |
| *pref_len = 0; |
| } |
| if (name) { |
| *name = NULL; |
| } |
| if (nam_len) { |
| *nam_len = 0; |
| } |
| |
| if (id[0] != '/') { |
| if (*is_relative != -1) { |
| return -parsed; |
| } else { |
| *is_relative = 1; |
| } |
| } else { |
| if (*is_relative == -1) { |
| *is_relative = 0; |
| } |
| ++parsed; |
| ++id; |
| } |
| |
| if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len)) < 1) { |
| return -parsed+ret; |
| } |
| |
| return parsed+ret; |
| } |
| |
| /** |
| * @brief Resolve (find) a prefix in a module include import. Does not log. |
| * |
| * @param[in] mod The module with the import. |
| * @param[in] prefix The prefix to find. |
| * @param[in] pref_len The prefix length. |
| * |
| * @return The matching module on success, NULL on error. |
| */ |
| static struct lys_module * |
| resolve_import_in_includes_recursive(struct lys_module *mod, const char *prefix, uint32_t pref_len) |
| { |
| int i, j; |
| struct lys_submodule *sub_mod; |
| struct lys_module *ret; |
| |
| for (i = 0; i < mod->inc_size; i++) { |
| sub_mod = mod->inc[i].submodule; |
| for (j = 0; j < sub_mod->imp_size; j++) { |
| if (!strncmp(sub_mod->imp[j].prefix, prefix, pref_len) && !sub_mod->imp[j].prefix[pref_len]) { |
| return sub_mod->imp[j].module; |
| } |
| } |
| } |
| |
| for (i = 0; i < mod->inc_size; i++) { |
| ret = resolve_import_in_includes_recursive((struct lys_module *)mod->inc[i].submodule, prefix, pref_len); |
| if (ret) { |
| return ret; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * @brief Resolve (find) a prefix in a module import. Does not log. |
| * |
| * @param[in] mod The module with the import. |
| * @param[in] prefix The prefix to find. |
| * @param[in] pref_len The prefix length. |
| * |
| * @return The matching module on success, NULL on error. |
| */ |
| static struct lys_module * |
| resolve_prefixed_module(struct lys_module *mod, const char *prefix, uint32_t pref_len) |
| { |
| int i; |
| |
| assert(prefix && pref_len); |
| |
| if (!mod) { |
| return NULL; |
| } |
| |
| /* module itself */ |
| if (!strncmp(mod->prefix, prefix, pref_len) && (mod->prefix[pref_len] == '\0')) { |
| return mod; |
| } |
| |
| /* imported modules */ |
| for (i = 0; i < mod->imp_size; i++) { |
| if (!strncmp(mod->imp[i].prefix, prefix, pref_len) && (mod->imp[i].prefix[pref_len] == '\0')) { |
| return mod->imp[i].module; |
| } |
| } |
| |
| /* imports in includes */ |
| return resolve_import_in_includes_recursive(mod, prefix, pref_len); |
| } |
| |
| /** |
| * @brief Resolves length or range intervals. Does not log. |
| * Syntax is assumed to be correct, *local_intv MUST be NULL. |
| * |
| * @param[in] str_restr The restriction as a string. |
| * @param[in] type The type of the restriction. |
| * @param[in] superior_restr Flag whether to check superior |
| * types. |
| * @param[out] local_intv The final interval structure. |
| * |
| * @return EXIT_SUCCESS on succes, -1 on error. |
| */ |
| int |
| resolve_len_ran_interval(const char *str_restr, struct lys_type *type, int superior_restr, |
| struct len_ran_intv** local_intv) |
| { |
| /* 0 - unsigned, 1 - signed, 2 - floating point */ |
| int kind, rc = EXIT_SUCCESS; |
| int64_t local_smin, local_smax; |
| uint64_t local_umin, local_umax; |
| long double local_fmin, local_fmax; |
| const char *seg_ptr, *ptr; |
| struct len_ran_intv *tmp_local_intv = NULL, *tmp_intv, *intv = NULL; |
| |
| switch (type->base) { |
| case LY_TYPE_BINARY: |
| kind = 0; |
| local_umin = 0; |
| local_umax = 18446744073709551615UL; |
| |
| if (!str_restr && type->info.binary.length) { |
| str_restr = type->info.binary.length->expr; |
| } |
| break; |
| case LY_TYPE_DEC64: |
| kind = 2; |
| local_fmin = -9223372036854775808.0; |
| local_fmin /= 1 << type->info.dec64.dig; |
| local_fmax = 9223372036854775807.0; |
| local_fmax /= 1 << type->info.dec64.dig; |
| |
| if (!str_restr && type->info.dec64.range) { |
| str_restr = type->info.dec64.range->expr; |
| } |
| break; |
| case LY_TYPE_INT8: |
| kind = 1; |
| local_smin = -128; |
| local_smax = 127; |
| |
| if (!str_restr && type->info.num.range) { |
| str_restr = type->info.num.range->expr; |
| } |
| break; |
| case LY_TYPE_INT16: |
| kind = 1; |
| local_smin = -32768; |
| local_smax = 32767; |
| |
| if (!str_restr && type->info.num.range) { |
| str_restr = type->info.num.range->expr; |
| } |
| break; |
| case LY_TYPE_INT32: |
| kind = 1; |
| local_smin = -2147483648; |
| local_smax = 2147483647; |
| |
| if (!str_restr && type->info.num.range) { |
| str_restr = type->info.num.range->expr; |
| } |
| break; |
| case LY_TYPE_INT64: |
| kind = 1; |
| local_smin = -9223372036854775807L - 1L; |
| local_smax = 9223372036854775807L; |
| |
| if (!str_restr && type->info.num.range) { |
| str_restr = type->info.num.range->expr; |
| } |
| break; |
| case LY_TYPE_UINT8: |
| kind = 0; |
| local_umin = 0; |
| local_umax = 255; |
| |
| if (!str_restr && type->info.num.range) { |
| str_restr = type->info.num.range->expr; |
| } |
| break; |
| case LY_TYPE_UINT16: |
| kind = 0; |
| local_umin = 0; |
| local_umax = 65535; |
| |
| if (!str_restr && type->info.num.range) { |
| str_restr = type->info.num.range->expr; |
| } |
| break; |
| case LY_TYPE_UINT32: |
| kind = 0; |
| local_umin = 0; |
| local_umax = 4294967295; |
| |
| if (!str_restr && type->info.num.range) { |
| str_restr = type->info.num.range->expr; |
| } |
| break; |
| case LY_TYPE_UINT64: |
| kind = 0; |
| local_umin = 0; |
| local_umax = 18446744073709551615UL; |
| |
| if (!str_restr && type->info.num.range) { |
| str_restr = type->info.num.range->expr; |
| } |
| break; |
| case LY_TYPE_STRING: |
| kind = 0; |
| local_umin = 0; |
| local_umax = 18446744073709551615UL; |
| |
| if (!str_restr && type->info.str.length) { |
| str_restr = type->info.str.length->expr; |
| } |
| break; |
| default: |
| LOGINT; |
| return -1; |
| } |
| |
| /* process superior types */ |
| if (type->der && superior_restr) { |
| if (resolve_len_ran_interval(NULL, &type->der->type, superior_restr, &intv)) { |
| LOGINT; |
| return -1; |
| } |
| assert(!intv || (intv->kind == kind)); |
| } |
| |
| if (!str_restr) { |
| /* we are validating data and not have any restriction, but a superior type might have */ |
| if (type->der && !superior_restr && !intv) { |
| if (resolve_len_ran_interval(NULL, &type->der->type, superior_restr, &intv)) { |
| LOGINT; |
| return -1; |
| } |
| assert(!intv || (intv->kind == kind)); |
| } |
| *local_intv = intv; |
| return EXIT_SUCCESS; |
| } |
| |
| /* adjust local min and max */ |
| if (intv) { |
| tmp_intv = intv; |
| |
| if (kind == 0) { |
| local_umin = tmp_intv->value.uval.min; |
| } else if (kind == 1) { |
| local_smin = tmp_intv->value.sval.min; |
| } else if (kind == 2) { |
| local_fmin = tmp_intv->value.fval.min; |
| } |
| |
| while (tmp_intv->next) { |
| tmp_intv = tmp_intv->next; |
| } |
| |
| if (kind == 0) { |
| local_umax = tmp_intv->value.uval.max; |
| } else if (kind == 1) { |
| local_smax = tmp_intv->value.sval.max; |
| } else if (kind == 2) { |
| local_fmax = tmp_intv->value.fval.max; |
| } |
| } |
| |
| /* finally parse our restriction */ |
| seg_ptr = str_restr; |
| while (1) { |
| if (!*local_intv && !tmp_local_intv) { |
| *local_intv = malloc(sizeof **local_intv); |
| tmp_local_intv = *local_intv; |
| } else { |
| tmp_local_intv->next = malloc(sizeof **local_intv); |
| tmp_local_intv = tmp_local_intv->next; |
| } |
| |
| tmp_local_intv->kind = kind; |
| tmp_local_intv->next = NULL; |
| |
| /* min */ |
| ptr = seg_ptr; |
| while (isspace(ptr[0])) { |
| ++ptr; |
| } |
| if (isdigit(ptr[0]) || (ptr[0] == '+') || (ptr[0] == '-')) { |
| if (kind == 0) { |
| tmp_local_intv->value.uval.min = atoll(ptr); |
| } else if (kind == 1) { |
| tmp_local_intv->value.sval.min = atoll(ptr); |
| } else if (kind == 2) { |
| tmp_local_intv->value.fval.min = atoll(ptr); |
| } |
| |
| if ((ptr[0] == '+') || (ptr[0] == '-')) { |
| ++ptr; |
| } |
| while (isdigit(ptr[0])) { |
| ++ptr; |
| } |
| } else if (!strncmp(ptr, "min", 3)) { |
| if (kind == 0) { |
| tmp_local_intv->value.uval.min = local_umin; |
| } else if (kind == 1) { |
| tmp_local_intv->value.sval.min = local_smin; |
| } else if (kind == 2) { |
| tmp_local_intv->value.fval.min = local_fmin; |
| } |
| |
| ptr += 3; |
| } else if (!strncmp(ptr, "max", 3)) { |
| if (kind == 0) { |
| tmp_local_intv->value.uval.min = local_umax; |
| } else if (kind == 1) { |
| tmp_local_intv->value.sval.min = local_smax; |
| } else if (kind == 2) { |
| tmp_local_intv->value.fval.min = local_fmax; |
| } |
| |
| ptr += 3; |
| } else { |
| LOGINT; |
| rc = -1; |
| goto cleanup; |
| } |
| |
| while (isspace(ptr[0])) { |
| ptr++; |
| } |
| |
| /* no interval or interval */ |
| if ((ptr[0] == '|') || !ptr[0]) { |
| if (kind == 0) { |
| tmp_local_intv->value.uval.max = tmp_local_intv->value.uval.min; |
| } else if (kind == 1) { |
| tmp_local_intv->value.sval.max = tmp_local_intv->value.sval.min; |
| } else if (kind == 2) { |
| tmp_local_intv->value.fval.max = tmp_local_intv->value.fval.min; |
| } |
| } else if (!strncmp(ptr, "..", 2)) { |
| /* skip ".." */ |
| ptr += 2; |
| while (isspace(ptr[0])) { |
| ++ptr; |
| } |
| |
| /* max */ |
| if (isdigit(ptr[0]) || (ptr[0] == '+') || (ptr[0] == '-')) { |
| if (kind == 0) { |
| tmp_local_intv->value.uval.max = atoll(ptr); |
| } else if (kind == 1) { |
| tmp_local_intv->value.sval.max = atoll(ptr); |
| } else if (kind == 2) { |
| tmp_local_intv->value.fval.max = atoll(ptr); |
| } |
| } else if (!strncmp(ptr, "max", 3)) { |
| if (kind == 0) { |
| tmp_local_intv->value.uval.max = local_umax; |
| } else if (kind == 1) { |
| tmp_local_intv->value.sval.max = local_smax; |
| } else if (kind == 2) { |
| tmp_local_intv->value.fval.max = local_fmax; |
| } |
| } else { |
| LOGINT; |
| rc = -1; |
| goto cleanup; |
| } |
| } else { |
| LOGINT; |
| rc = -1; |
| goto cleanup; |
| } |
| |
| /* next segment (next OR) */ |
| seg_ptr = strchr(seg_ptr, '|'); |
| if (!seg_ptr) { |
| break; |
| } |
| seg_ptr++; |
| } |
| |
| /* check local restrictions against superior ones */ |
| if (intv) { |
| tmp_intv = intv; |
| tmp_local_intv = *local_intv; |
| |
| while (tmp_local_intv && tmp_intv) { |
| /* reuse local variables */ |
| if (kind == 0) { |
| local_umin = tmp_local_intv->value.uval.min; |
| local_umax = tmp_local_intv->value.uval.max; |
| |
| /* it must be in this interval */ |
| if ((local_umin >= tmp_intv->value.uval.min) && (local_umin <= tmp_intv->value.uval.max)) { |
| /* this interval is covered, next one */ |
| if (local_umax <= tmp_intv->value.uval.max) { |
| tmp_local_intv = tmp_local_intv->next; |
| continue; |
| /* ascending order of restrictions -> fail */ |
| } else { |
| rc = -1; |
| goto cleanup; |
| } |
| } |
| } else if (kind == 1) { |
| local_smin = tmp_local_intv->value.sval.min; |
| local_smax = tmp_local_intv->value.sval.max; |
| |
| if ((local_smin >= tmp_intv->value.sval.min) && (local_smin <= tmp_intv->value.sval.max)) { |
| if (local_smax <= tmp_intv->value.sval.max) { |
| tmp_local_intv = tmp_local_intv->next; |
| continue; |
| } else { |
| rc = -1; |
| goto cleanup; |
| } |
| } |
| } else if (kind == 2) { |
| local_fmin = tmp_local_intv->value.fval.min; |
| local_fmax = tmp_local_intv->value.fval.max; |
| |
| if ((local_fmin >= tmp_intv->value.fval.min) && (local_fmin <= tmp_intv->value.fval.max)) { |
| if (local_fmax <= tmp_intv->value.fval.max) { |
| tmp_local_intv = tmp_local_intv->next; |
| continue; |
| } else { |
| rc = -1; |
| goto cleanup; |
| } |
| } |
| } |
| |
| tmp_intv = tmp_intv->next; |
| } |
| |
| /* some interval left uncovered -> fail */ |
| if (tmp_local_intv) { |
| rc = -1; |
| } |
| |
| } |
| |
| cleanup: |
| while (intv) { |
| tmp_intv = intv->next; |
| free(intv); |
| intv = tmp_intv; |
| } |
| |
| /* fail */ |
| if (rc) { |
| while (*local_intv) { |
| tmp_local_intv = (*local_intv)->next; |
| free(*local_intv); |
| *local_intv = tmp_local_intv; |
| } |
| } |
| |
| return rc; |
| } |
| |
| /** |
| * @brief Resolve a typedef. Does not log. |
| * |
| * @param[in] name Typedef name. |
| * @param[in] prefix Typedef name prefix. |
| * @param[in] module The main module. |
| * @param[in] parent The parent of the resolved type definition. |
| * @param[out] ret Pointer to the resolved typedef. Can be NULL. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| int |
| resolve_superior_type(const char *name, const char *prefix, struct lys_module *module, struct lys_node *parent, |
| struct lys_tpdf **ret) |
| { |
| int i, j; |
| struct lys_tpdf *tpdf; |
| int tpdf_size; |
| |
| if (!prefix) { |
| /* no prefix, try built-in types */ |
| for (i = 1; i < LY_DATA_TYPE_COUNT; i++) { |
| if (!strcmp(ly_types[i].def->name, name)) { |
| if (ret) { |
| *ret = ly_types[i].def; |
| } |
| return EXIT_SUCCESS; |
| } |
| } |
| } else { |
| if (!strcmp(prefix, module->prefix)) { |
| /* prefix refers to the current module, ignore it */ |
| prefix = NULL; |
| } |
| } |
| |
| if (!prefix && parent) { |
| /* search in local typedefs */ |
| while (parent) { |
| switch (parent->nodetype) { |
| case LYS_CONTAINER: |
| tpdf_size = ((struct lys_node_container *)parent)->tpdf_size; |
| tpdf = ((struct lys_node_container *)parent)->tpdf; |
| break; |
| |
| case LYS_LIST: |
| tpdf_size = ((struct lys_node_list *)parent)->tpdf_size; |
| tpdf = ((struct lys_node_list *)parent)->tpdf; |
| break; |
| |
| case LYS_GROUPING: |
| tpdf_size = ((struct lys_node_grp *)parent)->tpdf_size; |
| tpdf = ((struct lys_node_grp *)parent)->tpdf; |
| break; |
| |
| case LYS_RPC: |
| tpdf_size = ((struct lys_node_rpc *)parent)->tpdf_size; |
| tpdf = ((struct lys_node_rpc *)parent)->tpdf; |
| break; |
| |
| case LYS_NOTIF: |
| tpdf_size = ((struct lys_node_notif *)parent)->tpdf_size; |
| tpdf = ((struct lys_node_notif *)parent)->tpdf; |
| break; |
| |
| case LYS_INPUT: |
| case LYS_OUTPUT: |
| tpdf_size = ((struct lys_node_rpc_inout *)parent)->tpdf_size; |
| tpdf = ((struct lys_node_rpc_inout *)parent)->tpdf; |
| break; |
| |
| default: |
| parent = parent->parent; |
| continue; |
| } |
| |
| for (i = 0; i < tpdf_size; i++) { |
| if (!strcmp(tpdf[i].name, name)) { |
| if (ret) { |
| *ret = &tpdf[i]; |
| } |
| return EXIT_SUCCESS; |
| } |
| } |
| |
| parent = parent->parent; |
| } |
| } else if (prefix) { |
| /* get module where to search */ |
| module = resolve_prefixed_module(module, prefix, strlen(prefix)); |
| if (!module) { |
| return -1; |
| } |
| } |
| |
| /* search in top level typedefs */ |
| for (i = 0; i < module->tpdf_size; i++) { |
| if (!strcmp(module->tpdf[i].name, name)) { |
| if (ret) { |
| *ret = &module->tpdf[i]; |
| } |
| return EXIT_SUCCESS; |
| } |
| } |
| |
| /* search in submodules */ |
| for (i = 0; i < module->inc_size; i++) { |
| for (j = 0; j < module->inc[i].submodule->tpdf_size; j++) { |
| if (!strcmp(module->inc[i].submodule->tpdf[j].name, name)) { |
| if (ret) { |
| *ret = &module->inc[i].submodule->tpdf[j]; |
| } |
| return EXIT_SUCCESS; |
| } |
| } |
| } |
| |
| return EXIT_FAILURE; |
| } |
| |
| /* logs directly */ |
| static int |
| check_default(struct lys_type *type, const char *value) |
| { |
| /* TODO - RFC 6020, sec. 7.3.4 */ |
| (void)type; |
| (void)value; |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Check a key for mandatory attributes. Logs directly. |
| * |
| * @param[in] key The key to check. |
| * @param[in] flags What flags to check. |
| * @param[in] list The list of all the keys. |
| * @param[in] index Index of the key in the key list. |
| * @param[in] name The name of the keys. |
| * @param[in] len The name length. |
| * @param[in] line The line in the input file. |
| * |
| * @return EXIT_SUCCESS on success, -1 on error. |
| */ |
| static int |
| check_key(struct lys_node_leaf *key, uint8_t flags, struct lys_node_leaf **list, int index, const char *name, int len, |
| uint32_t line) |
| { |
| char *dup = NULL; |
| int j; |
| |
| /* existence */ |
| if (!key) { |
| if (name[len] != '\0') { |
| dup = strdup(name); |
| dup[len] = '\0'; |
| name = dup; |
| } |
| LOGVAL(LYE_KEY_MISS, line, name); |
| free(dup); |
| return -1; |
| } |
| |
| /* uniqueness */ |
| for (j = index - 1; j >= 0; j--) { |
| if (list[index] == list[j]) { |
| LOGVAL(LYE_KEY_DUP, line, key->name); |
| return -1; |
| } |
| } |
| |
| /* key is a leaf */ |
| if (key->nodetype != LYS_LEAF) { |
| LOGVAL(LYE_KEY_NLEAF, line, key->name); |
| return -1; |
| } |
| |
| /* type of the leaf is not built-in empty */ |
| if (key->type.base == LY_TYPE_EMPTY) { |
| LOGVAL(LYE_KEY_TYPE, line, key->name); |
| return -1; |
| } |
| |
| /* config attribute is the same as of the list */ |
| if ((flags & LYS_CONFIG_MASK) != (key->flags & LYS_CONFIG_MASK)) { |
| LOGVAL(LYE_KEY_CONFIG, line, key->name); |
| return -1; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Resolve (fill) a unique. Logs directly. |
| * |
| * @param[in] parent The parent node of the unique structure. |
| * @param[in] uniq_str The value of the unique node. |
| * @param[in] uniq_s The unique structure to use. |
| * @param[in] first Whether this is the first resolution try. Affects logging. |
| * @param[in] line The line in the input file. |
| * |
| * @return EXIT_SUCCESS on succes, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| int |
| resolve_unique(struct lys_node *parent, const char *uniq_str, struct lys_unique *uniq_s, int first, uint32_t line) |
| { |
| char *uniq_val, *uniq_begin, *start; |
| int i, j, rc; |
| |
| /* count the number of unique values */ |
| uniq_val = uniq_begin = strdup(uniq_str); |
| uniq_s->leafs_size = 0; |
| while ((uniq_val = strpbrk(uniq_val, " \t\n"))) { |
| uniq_s->leafs_size++; |
| while (isspace(*uniq_val)) { |
| uniq_val++; |
| } |
| } |
| uniq_s->leafs_size++; |
| uniq_s->leafs = calloc(uniq_s->leafs_size, sizeof *uniq_s->leafs); |
| |
| /* interconnect unique values with the leafs */ |
| uniq_val = uniq_begin; |
| for (i = 0; uniq_val && i < uniq_s->leafs_size; i++) { |
| start = uniq_val; |
| if ((uniq_val = strpbrk(start, " \t\n"))) { |
| *uniq_val = '\0'; /* add terminating NULL byte */ |
| uniq_val++; |
| while (isspace(*uniq_val)) { |
| uniq_val++; |
| } |
| } /* else only one nodeid present/left already NULL byte terminated */ |
| |
| rc = resolve_schema_nodeid(start, parent->child, parent->module, LYS_LEAF, |
| (struct lys_node **)&uniq_s->leafs[i]); |
| if (rc) { |
| if ((rc == -1) || !first) { |
| LOGVAL(LYE_INARG, line, start, "unique"); |
| if (rc == EXIT_FAILURE) { |
| LOGVAL(LYE_SPEC, 0, "Target leaf not found."); |
| } |
| } |
| goto error; |
| } |
| if (uniq_s->leafs[i]->nodetype != LYS_LEAF) { |
| LOGVAL(LYE_INARG, line, start, "unique"); |
| LOGVAL(LYE_SPEC, 0, "Target is not a leaf."); |
| rc = -1; |
| goto error; |
| } |
| |
| for (j = 0; j < i; j++) { |
| if (uniq_s->leafs[j] == uniq_s->leafs[i]) { |
| LOGVAL(LYE_INARG, line, start, "unique"); |
| LOGVAL(LYE_SPEC, 0, "The identifier is not unique"); |
| rc = -1; |
| goto error; |
| } |
| } |
| } |
| |
| free(uniq_begin); |
| return EXIT_SUCCESS; |
| |
| error: |
| |
| free(uniq_s->leafs); |
| free(uniq_begin); |
| |
| return rc; |
| } |
| |
| /** |
| * @brief Resolve (fill) a grouping in an uses. Logs directly. |
| * |
| * @param[in] uses The uses to use. |
| * @param[in] first Whether this is the first resolution try. Affects logging. |
| * @param[in] line The line in the input file. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| static int |
| resolve_grouping(struct lys_node_uses *uses, int first, uint32_t line) |
| { |
| struct lys_module *module; |
| const char *prefix, *name; |
| int i, pref_len, nam_len; |
| struct lys_node *start; |
| |
| /* parse the identifier, it must be parsed on one call */ |
| if ((i = parse_node_identifier(uses->name, &prefix, &pref_len, &name, &nam_len)) < 1) { |
| LOGVAL(LYE_INCHAR, line, uses->name[-i], &uses->name[-i]); |
| return -1; |
| } else if (uses->name[i]) { |
| LOGVAL(LYE_INCHAR, line, uses->name[i], &uses->name[i]); |
| return -1; |
| } |
| |
| if (prefix) { |
| module = resolve_prefixed_module(uses->module, prefix, pref_len); |
| if (!module) { |
| LOGVAL(LYE_INPREF_LEN, line, pref_len, prefix); |
| return -1; |
| } |
| start = module->data; |
| } else { |
| start = (struct lys_node *)uses; |
| } |
| |
| uses->grp = lys_find_grouping_up(name, start, 1); |
| if (uses->grp) { |
| return EXIT_SUCCESS; |
| } |
| |
| if (prefix || !first) { |
| LOGVAL(LYE_INRESOLV, line, "grouping", uses->name); |
| } |
| /* import must now be fully resolved */ |
| if (prefix) { |
| return -1; |
| } |
| return EXIT_FAILURE; |
| } |
| |
| /** |
| * @brief Resolve (find) a feature definition. Logs directly. |
| * |
| * @param[in] name Feature name. |
| * @param[in] module Module to search in. |
| * @param[in] first Whether this is the first resolution try. Affects logging. |
| * @param[in] line The line in the input file. |
| * @param[out] ret Pointer to the resolved feature. Can be NULL. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| static int |
| resolve_feature(const char *id, struct lys_module *module, int first, uint32_t line, struct lys_feature **ret) |
| { |
| const char *prefix, *name; |
| int pref_len, nam_len, i, j; |
| |
| assert(id); |
| assert(module); |
| |
| /* check prefix */ |
| if ((i = parse_node_identifier(id, &prefix, &pref_len, &name, &nam_len)) < 1) { |
| LOGVAL(LYE_INCHAR, line, id[-i], &id[-i]); |
| return -1; |
| } |
| |
| if (prefix) { |
| /* search in imported modules */ |
| module = resolve_prefixed_module(module, prefix, pref_len); |
| if (!module) { |
| /* identity refers unknown data model */ |
| LOGVAL(LYE_INPREF_LEN, line, pref_len, prefix); |
| return -1; |
| } |
| } else { |
| /* search in submodules */ |
| for (i = 0; i < module->inc_size; i++) { |
| for (j = 0; j < module->inc[i].submodule->features_size; j++) { |
| if (!strcmp(name, module->inc[i].submodule->features[j].name)) { |
| if (ret) { |
| *ret = &(module->inc[i].submodule->features[j]); |
| } |
| return EXIT_SUCCESS; |
| } |
| } |
| } |
| } |
| |
| /* search in the identified module */ |
| for (j = 0; j < module->features_size; j++) { |
| if (!strcmp(name, module->features[j].name)) { |
| if (ret) { |
| *ret = &module->features[j]; |
| } |
| return EXIT_SUCCESS; |
| } |
| } |
| |
| /* not found */ |
| if (!first) { |
| LOGVAL(LYE_INRESOLV, line, "feature", id); |
| } |
| return EXIT_FAILURE; |
| } |
| |
| /** |
| * @brief Resolve (find) a valid sibling. Does not log. |
| * |
| * Valid child means a schema pointer to a node that is part of |
| * the data meaning uses are skipped. Includes module comparison |
| * (can handle augments). Module is adjusted based on the prefix. |
| * Includes are also searched if siblings are top-level nodes. |
| * |
| * @param[in] mod Main module. Prefix is considered to be from this module. |
| * @param[in] siblings Siblings to consider. They are first adjusted to |
| * point to the first sibling. |
| * @param[in] prefix Node prefix. |
| * @param[in] pref_len Node prefix length. |
| * @param[in] name Node name. |
| * @param[in] nam_len Node name length. |
| * @param[in] type ORed desired type of the node. 0 means any type. |
| * Returns only schema data nodes (no uses, grouping, augment, choice, case). |
| * @param[out] ret Pointer to the node of the desired type. Can be NULL. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| int |
| resolve_sibling(struct lys_module *mod, struct lys_node *siblings, const char *prefix, int pref_len, const char *name, |
| int nam_len, LYS_NODE type, struct lys_node **ret) |
| { |
| struct lys_node *node, *old_siblings = NULL; |
| struct lys_module *prefix_mod, *cur_mod; |
| int in_submod; |
| |
| assert(siblings && name); |
| assert(!(type & LYS_USES)); |
| |
| /* find the beginning */ |
| while (siblings->prev->next) { |
| siblings = siblings->prev; |
| } |
| |
| /* fill the name length in case the caller is so indifferent */ |
| if (!nam_len) { |
| nam_len = strlen(name); |
| } |
| |
| /* we start with the module itself, submodules come later */ |
| in_submod = 0; |
| |
| /* set prefix_mod correctly */ |
| if (prefix) { |
| prefix_mod = resolve_prefixed_module(mod, prefix, pref_len); |
| if (!prefix_mod) { |
| return -1; |
| } |
| cur_mod = prefix_mod; |
| /* it is not our module */ |
| if (cur_mod != mod) { |
| old_siblings = siblings; |
| siblings = cur_mod->data; |
| } |
| } else { |
| if (mod) { |
| prefix_mod = mod; |
| } else { |
| prefix_mod = siblings->module; |
| } |
| if (prefix_mod->type) { |
| prefix_mod = ((struct lys_submodule *)prefix_mod)->belongsto; |
| } |
| cur_mod = prefix_mod; |
| } |
| |
| while (1) { |
| /* try to find the node */ |
| node = NULL; |
| while ((node = lys_getnext(node, siblings->parent, cur_mod, 0))) { |
| if (!type || (node->nodetype & type)) { |
| /* module check */ |
| if (!node->module->type) { |
| if (cur_mod != node->module) { |
| continue; |
| } |
| } else { |
| /* both are submodules */ |
| if (cur_mod->type) { |
| if (cur_mod != node->module) { |
| continue; |
| } |
| } else { |
| if (cur_mod != ((struct lys_submodule *)node->module)->belongsto) { |
| continue; |
| } |
| } |
| } |
| |
| /* direct name check */ |
| if (node->name == name || (!strncmp(node->name, name, nam_len) && !node->name[nam_len])) { |
| if (ret) { |
| *ret = node; |
| } |
| return EXIT_SUCCESS; |
| } |
| } |
| } |
| |
| /* The original siblings may be valid, |
| * it's a special case when we're looking |
| * for a node from augment. |
| */ |
| if (old_siblings) { |
| siblings = old_siblings; |
| old_siblings = NULL; |
| continue; |
| } |
| |
| /* we're not top-level, search ended */ |
| if (siblings->parent) { |
| break; |
| } |
| |
| /* let's try the submodules */ |
| if (in_submod == prefix_mod->inc_size) { |
| break; |
| } |
| cur_mod = (struct lys_module *)prefix_mod->inc[in_submod].submodule; |
| siblings = cur_mod->data; |
| ++in_submod; |
| } |
| |
| return EXIT_FAILURE; |
| } |
| |
| /** |
| * @brief Resolve (find) a schema node based on a schema-nodeid. Does not log. |
| * |
| * node_type - LYS_AUGMENT (searches also RPCs and notifications) |
| * - LYS_USES (only descendant-schema-nodeid allowed, ".." not allowed, always return a grouping) |
| * - LYS_CHOICE (search only start->child, only descendant-schema-nodeid allowed) |
| * - LYS_LEAF (like LYS_USES, but always returns a data node) |
| * |
| * If id is absolute, start is ignored. If id is relative, start must be the first child to be searched |
| * continuing with its siblings. |
| * |
| * @param[in] id Schema-nodeid string. |
| * @param[in] start Start of the relative search. |
| * @param[in] mod Module to use. |
| * @param[in] node_type Decides how to modify the search. |
| * @param[out] ret Pointer to the matching node. Can be NULL. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| int |
| resolve_schema_nodeid(const char *id, struct lys_node *start, struct lys_module *mod, LYS_NODE node_type, |
| struct lys_node **ret) |
| { |
| const char *name, *prefix; |
| struct lys_node *sibling; |
| int i, nam_len, pref_len, is_relative = -1; |
| struct lys_module *prefix_mod, *start_mod; |
| /* 0 - in module, 1 - in 1st submodule, 2 - in 2nd submodule, ... */ |
| uint8_t in_submod = 0; |
| |
| assert(mod); |
| assert(id); |
| |
| if ((i = parse_schema_nodeid(id, &prefix, &pref_len, &name, &nam_len, &is_relative)) < 1) { |
| return -1; |
| } |
| id += i; |
| |
| if (!is_relative && (node_type & (LYS_USES | LYS_CHOICE | LYS_LEAF))) { |
| return -1; |
| } |
| |
| /* absolute-schema-nodeid */ |
| if (!is_relative) { |
| if (prefix) { |
| start_mod = resolve_prefixed_module(mod, prefix, pref_len); |
| if (!start_mod) { |
| return -1; |
| } |
| start = start_mod->data; |
| } else { |
| start = mod->data; |
| start_mod = mod; |
| } |
| /* descendant-schema-nodeid */ |
| } else { |
| if (start) { |
| start_mod = start->module; |
| } else { |
| start_mod = mod; |
| } |
| } |
| |
| while (1) { |
| sibling = NULL; |
| LY_TREE_FOR(start, sibling) { |
| /* name match */ |
| if (((sibling->nodetype != LYS_GROUPING) || (node_type == LYS_USES)) |
| && (!(sibling->nodetype & (LYS_RPC | LYS_NOTIF)) || (node_type == LYS_AUGMENT)) |
| && ((sibling->name && !strncmp(name, sibling->name, nam_len) && !sibling->name[nam_len]) |
| || (!strncmp(name, "input", 5) && (nam_len == 5) && (sibling->nodetype == LYS_INPUT)) |
| || (!strncmp(name, "output", 6) && (nam_len == 6) && (sibling->nodetype == LYS_OUTPUT)))) { |
| |
| /* prefix match check */ |
| if (prefix) { |
| prefix_mod = resolve_prefixed_module(mod, prefix, pref_len); |
| if (!prefix_mod) { |
| return -1; |
| } |
| } else { |
| prefix_mod = mod; |
| if (prefix_mod->type) { |
| prefix_mod = ((struct lys_submodule *)prefix_mod)->belongsto; |
| } |
| } |
| |
| /* modules need to always be checked, we want to skip augments */ |
| if (!sibling->module->type) { |
| if (prefix_mod != sibling->module) { |
| continue; |
| } |
| } else { |
| if (prefix_mod != ((struct lys_submodule *)sibling->module)->belongsto) { |
| continue; |
| } |
| } |
| |
| /* the result node? */ |
| if (!id[0]) { |
| /* we're looking only for groupings, this is a data node */ |
| if ((node_type == LYS_USES) && (sibling->nodetype != LYS_GROUPING)) { |
| continue; |
| } |
| if (ret) { |
| *ret = sibling; |
| } |
| return EXIT_SUCCESS; |
| } |
| |
| /* we're looking for a grouping (node_type == LYS_USES), |
| * but this isn't it, we cannot search inside |
| */ |
| if (sibling->nodetype == LYS_GROUPING) { |
| continue; |
| } |
| |
| /* check for shorthand cases - then 'start' does not change */ |
| if (!sibling->parent || (sibling->parent->nodetype != LYS_CHOICE) |
| || (sibling->nodetype == LYS_CASE)) { |
| start = sibling->child; |
| } |
| break; |
| } |
| } |
| |
| /* we did not find the case in direct siblings */ |
| if (node_type == LYS_CHOICE) { |
| return -1; |
| } |
| |
| /* no match */ |
| if (!sibling) { |
| /* are we done with the included submodules as well? */ |
| if (in_submod == start_mod->inc_size) { |
| return EXIT_FAILURE; |
| } |
| |
| /* we aren't, check the next one */ |
| ++in_submod; |
| start = start_mod->inc[in_submod-1].submodule->data; |
| continue; |
| } |
| |
| /* we found our submodule */ |
| if (in_submod) { |
| start_mod = (struct lys_module *)start_mod->inc[in_submod-1].submodule; |
| in_submod = 0; |
| } |
| |
| if ((i = parse_schema_nodeid(id, &prefix, &pref_len, &name, &nam_len, &is_relative)) < 1) { |
| return -1; |
| } |
| id += i; |
| } |
| |
| /* cannot get here */ |
| LOGINT; |
| return -1; |
| } |
| |
| /* ignores line */ |
| static void |
| unres_data_del(struct unres_data *unres, uint32_t i) |
| { |
| /* there are items after the one deleted */ |
| if (i+1 < unres->count) { |
| /* we only move the data, memory is left allocated, why bother */ |
| memmove(&unres->node[i], &unres->node[i+1], (unres->count-(i+1)) * sizeof *unres->node); |
| |
| /* deleting the last item */ |
| } else if (i == 0) { |
| free(unres->node); |
| unres->node = NULL; |
| } |
| |
| /* if there are no items after and it is not the last one, just move the counter */ |
| --unres->count; |
| } |
| |
| /** |
| * @brief Resolve (find) a data node from a specific module. Does not log. |
| * |
| * @param[in] mod Module to search in. |
| * @param[in] name Name of the data node. |
| * @param[in] nam_len Length of the name. |
| * @param[in] start Data node to start the search from. |
| * @param[in,out] parents Resolved nodes. If there are some parents, |
| * they are replaced (!!) with the resolvents. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference. |
| */ |
| static int |
| resolve_data(struct lys_module *mod, const char *name, int nam_len, struct lyd_node *start, struct unres_data *parents) |
| { |
| struct lyd_node *node; |
| int flag; |
| uint32_t i; |
| |
| if (!parents->count) { |
| parents->count = 1; |
| parents->node = malloc(sizeof *parents->node); |
| parents->node[0] = NULL; |
| } |
| for (i = 0; i < parents->count;) { |
| if (parents->node[i] && (parents->node[i]->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML))) { |
| /* skip */ |
| ++i; |
| continue; |
| } |
| flag = 0; |
| LY_TREE_FOR(parents->node[i] ? parents->node[i]->child : start, node) { |
| if (node->schema->module == mod && !strncmp(node->schema->name, name, nam_len) |
| && node->schema->name[nam_len] == '\0') { |
| /* matching target */ |
| if (!flag) { |
| /* put node instead of the current parent */ |
| parents->node[i] = node; |
| flag = 1; |
| } else { |
| /* multiple matching, so create a new node */ |
| ++parents->count; |
| parents->node = realloc(parents->node, parents->count * sizeof *parents->node); |
| parents->node[parents->count-1] = node; |
| ++i; |
| } |
| } |
| } |
| |
| if (!flag) { |
| /* remove item from the parents list */ |
| unres_data_del(parents, i); |
| } else { |
| ++i; |
| } |
| } |
| |
| return parents->count ? EXIT_SUCCESS : EXIT_FAILURE; |
| } |
| |
| /** |
| * @brief Resolve (find) a data node. Does not log. |
| * |
| * @param[in] prefix Prefix of the data node. |
| * @param[in] pref_len Length of the prefix. |
| * @param[in] name Name of the data node. |
| * @param[in] nam_len Length of the name. |
| * @param[in] start Data node to start the search from. |
| * @param[in,out] parents Resolved nodes. If there are some parents, |
| * they are replaced (!!) with the resolvents. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 otherwise. |
| */ |
| static int |
| resolve_data_nodeid(const char *prefix, int pref_len, const char *name, int name_len, struct lyd_node *start, |
| struct unres_data *parents) |
| { |
| struct lys_module *mod; |
| |
| assert(start); |
| |
| if (prefix) { |
| /* we have prefix, find appropriate module */ |
| mod = resolve_prefixed_module(start->schema->module, prefix, pref_len); |
| if (!mod) { |
| /* invalid prefix */ |
| return -1; |
| } |
| } else { |
| /* no prefix, module is the same as of current node */ |
| mod = start->schema->module; |
| } |
| |
| return resolve_data(mod, name, name_len, start, parents); |
| } |
| |
| /** |
| * @brief Resolve a path predicate (leafref) in data context. Logs directly |
| * only specific errors, general no-resolvent error is left to the caller, |
| * but line fail is always displayed. |
| * |
| * @param[in] pred Predicate to use. |
| * @param[in] first Whether this is the first resolution try. Affects logging. |
| * @param[in] line Line in the input file. |
| * @param[in,out] node_match Nodes satisfying the restriction |
| * without the predicate. Nodes not |
| * satisfying the predicate are removed. |
| * @param[out] parsed Number of characters parsed, negative on error. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| static int |
| resolve_path_predicate_data(const char *pred, int first, uint32_t line, struct unres_data *node_match, int *parsed) |
| { |
| /* ... /node[source = destination] ... */ |
| struct unres_data source_match, dest_match; |
| const char *path_key_expr, *source, *sour_pref, *dest, *dest_pref; |
| int pke_len, sour_len, sour_pref_len, dest_len, dest_pref_len, parsed_loc = 0, pke_parsed = 0; |
| int has_predicate, dest_parent_times, i, rc; |
| uint32_t j; |
| |
| source_match.count = 1; |
| source_match.node = malloc(sizeof *source_match.node); |
| dest_match.count = 1; |
| dest_match.node = malloc(sizeof *dest_match.node); |
| |
| do { |
| if ((i = parse_path_predicate(pred, &sour_pref, &sour_pref_len, &source, &sour_len, &path_key_expr, |
| &pke_len, &has_predicate)) < 1) { |
| LOGVAL(LYE_INCHAR, line, pred[-i], &pred[-i]); |
| rc = -1; |
| goto error; |
| } |
| parsed_loc += i; |
| pred += i; |
| |
| for (j = 0; j < node_match->count;) { |
| /* source */ |
| source_match.node[0] = node_match->node[j]; |
| |
| /* must be leaf (key of a list) */ |
| if ((rc = resolve_data_nodeid(sour_pref, sour_pref_len, source, sour_len, node_match->node[j], |
| &source_match)) || (source_match.count != 1) || (source_match.node[0]->schema->nodetype != LYS_LEAF)) { |
| /* general error, the one written later will suffice */ |
| if ((rc == -1) || !first) { |
| LOGVAL(LYE_LINE, line); |
| } |
| i = 0; |
| goto error; |
| } |
| |
| /* destination */ |
| dest_match.node[0] = node_match->node[j]; |
| dest_parent_times = 0; |
| if ((i = parse_path_key_expr(path_key_expr, &dest_pref, &dest_pref_len, &dest, &dest_len, |
| &dest_parent_times)) < 1) { |
| LOGVAL(LYE_INCHAR, line, path_key_expr[-i], &path_key_expr[-i]); |
| rc = -1; |
| goto error; |
| } |
| pke_parsed = i; |
| for (i = 0; i < dest_parent_times; ++i) { |
| dest_match.node[0] = dest_match.node[0]->parent; |
| if (!dest_match.node[0]) { |
| /* general error, the one written later will suffice */ |
| if (!first) { |
| LOGVAL(LYE_LINE, line); |
| } |
| i = 0; |
| rc = EXIT_FAILURE; |
| goto error; |
| } |
| } |
| while (1) { |
| if ((rc = resolve_data_nodeid(dest_pref, dest_pref_len, dest, dest_len, dest_match.node[0], |
| &dest_match)) || (dest_match.count != 1)) { |
| /* general error, the one written later will suffice */ |
| if ((rc == -1) || !first) { |
| LOGVAL(LYE_LINE, line); |
| } |
| i = 0; |
| goto error; |
| } |
| |
| if (pke_len == pke_parsed) { |
| break; |
| } |
| if ((i = parse_path_key_expr(path_key_expr+pke_parsed, &dest_pref, &dest_pref_len, &dest, &dest_len, |
| &dest_parent_times)) < 1) { |
| LOGVAL(LYE_INCHAR, line, path_key_expr[-i], &path_key_expr[-i]); |
| rc = -1; |
| goto error; |
| } |
| pke_parsed += i; |
| } |
| |
| /* check match between source and destination nodes */ |
| if (((struct lys_node_leaf *)source_match.node[0]->schema)->type.base |
| != ((struct lys_node_leaf *)dest_match.node[0]->schema)->type.base) { |
| goto remove_leafref; |
| } |
| |
| if (((struct lyd_node_leaf_list *)source_match.node[0])->value_str |
| != ((struct lyd_node_leaf_list *)dest_match.node[0])->value_str) { |
| goto remove_leafref; |
| } |
| |
| /* leafref is ok, continue check with next leafref */ |
| ++j; |
| continue; |
| |
| remove_leafref: |
| /* does not fulfill conditions, remove leafref record */ |
| unres_data_del(node_match, j); |
| } |
| } while (has_predicate); |
| |
| free(source_match.node); |
| free(dest_match.node); |
| if (parsed) { |
| *parsed = parsed_loc; |
| } |
| return EXIT_SUCCESS; |
| |
| error: |
| |
| if (source_match.count) { |
| free(source_match.node); |
| } |
| if (dest_match.count) { |
| free(dest_match.node); |
| } |
| if (parsed) { |
| *parsed = -parsed_loc+i; |
| } |
| return rc; |
| } |
| |
| /** |
| * @brief Resolve a path (leafref) in data context. Logs directly. |
| * |
| * @param[in] node Leafref data node. |
| * @param[in] path Path of the leafref. |
| * @param[in] first Whether this is the first resolution try. Affects logging. |
| * @param[in] line Line in the input file. |
| * @param[out] ret Matching nodes. Expects an empty, but allocated structure. Lines left untouched. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 otherwise. |
| */ |
| static int |
| resolve_path_arg_data(struct lyd_node *node, const char *path, int first, uint32_t line, struct unres_data *ret) |
| { |
| struct lyd_node *data = NULL; |
| const char *prefix, *name; |
| int pref_len, nam_len, has_predicate, parent_times, i, parsed, rc; |
| uint32_t j; |
| |
| assert(node && path && ret && !ret->count); |
| |
| parent_times = 0; |
| parsed = 0; |
| |
| /* searching for nodeset */ |
| do { |
| if ((i = parse_path_arg(path, &prefix, &pref_len, &name, &nam_len, &parent_times, &has_predicate)) < 1) { |
| LOGVAL(LYE_INCHAR, line, path[-i], &path[-i]); |
| rc = -1; |
| goto error; |
| } |
| path += i; |
| parsed += i; |
| |
| if (!ret->count) { |
| if (parent_times != -1) { |
| ret->count = 1; |
| ret->node = calloc(1, sizeof *ret->node); |
| } |
| for (i = 0; i < parent_times; ++i) { |
| /* relative path */ |
| if (!ret->count) { |
| /* error, too many .. */ |
| LOGVAL(LYE_INVAL, line, path, node->schema->name); |
| rc = -1; |
| goto error; |
| } else if (!ret->node[0]) { |
| /* first .. */ |
| data = ret->node[0] = node->parent; |
| } else if (!ret->node[0]->parent) { |
| /* we are in root */ |
| ret->count = 0; |
| free(ret->node); |
| ret->node = NULL; |
| } else { |
| /* multiple .. */ |
| data = ret->node[0] = ret->node[0]->parent; |
| } |
| } |
| |
| /* absolute path */ |
| if (parent_times == -1) { |
| for (data = node; data->parent; data = data->parent); |
| /* we're still parsing it and the pointer is not correct yet */ |
| if (data->prev) { |
| for (; data->prev->next; data = data->prev); |
| } |
| } |
| } |
| |
| /* node identifier */ |
| if ((rc = resolve_data_nodeid(prefix, pref_len, name, nam_len, data, ret))) { |
| if ((rc == -1) || !first) { |
| LOGVAL(LYE_INELEM_LEN, line, nam_len, name); |
| } |
| goto error; |
| } |
| |
| if (has_predicate) { |
| /* we have predicate, so the current results must be lists */ |
| for (j = 0; j < ret->count;) { |
| if (ret->node[j]->schema->nodetype == LYS_LIST && |
| ((struct lys_node_list *)ret->node[0]->schema)->keys) { |
| /* leafref is ok, continue check with next leafref */ |
| ++j; |
| continue; |
| } |
| |
| /* does not fulfill conditions, remove leafref record */ |
| unres_data_del(ret, j); |
| } |
| if ((rc = resolve_path_predicate_data(path, first, line, ret, &i))) { |
| /* line was already displayed */ |
| if ((rc == -1) || !first) { |
| LOGVAL(LYE_NORESOLV, 0, path); |
| } |
| goto error; |
| } |
| path += i; |
| parsed += i; |
| |
| if (!ret->count) { |
| if (!first) { |
| LOGVAL(LYE_NORESOLV, line, path-parsed); |
| } |
| rc = EXIT_FAILURE; |
| goto error; |
| } |
| } |
| } while (path[0] != '\0'); |
| |
| return EXIT_SUCCESS; |
| |
| error: |
| |
| free(ret->node); |
| ret->node = NULL; |
| ret->count = 0; |
| |
| return rc; |
| } |
| |
| /** |
| * @brief Resolve a path (leafref) predicate in schema context. Logs directly. |
| * |
| * @param[in] path Path to use. |
| * @param[in] mod Schema module. |
| * @param[in] source_node Left operand node. |
| * @param[in] dest_node Right ooperand node. |
| * @param[in] first Whether this is the first resolution try. Affects logging. |
| * @param[in] line Line in the input file. |
| * |
| * @return 0 on forward reference, otherwise the number |
| * of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| static int |
| resolve_path_predicate_schema(const char *path, struct lys_module *mod, struct lys_node *source_node, |
| struct lys_node *dest_node, int first, uint32_t line) |
| { |
| struct lys_node *src_node, *dst_node; |
| const char *path_key_expr, *source, *sour_pref, *dest, *dest_pref; |
| int pke_len, sour_len, sour_pref_len, dest_len, dest_pref_len, parsed = 0, pke_parsed = 0; |
| int has_predicate, dest_parent_times = 0, i, rc; |
| |
| do { |
| if ((i = parse_path_predicate(path, &sour_pref, &sour_pref_len, &source, &sour_len, &path_key_expr, |
| &pke_len, &has_predicate)) < 1) { |
| LOGVAL(LYE_INCHAR, line, path[-i], path-i); |
| return -parsed+i; |
| } |
| parsed += i; |
| path += i; |
| |
| /* source (must be leaf) */ |
| rc = resolve_sibling(mod, source_node->child, sour_pref, sour_pref_len, source, sour_len, LYS_LEAF, &src_node); |
| if (rc) { |
| if ((rc == -1) || !first) { |
| LOGVAL(LYE_NORESOLV, line, path-parsed); |
| } |
| return 0; |
| } |
| |
| /* destination */ |
| if ((i = parse_path_key_expr(path_key_expr, &dest_pref, &dest_pref_len, &dest, &dest_len, |
| &dest_parent_times)) < 1) { |
| LOGVAL(LYE_INCHAR, line, path_key_expr[-i], path_key_expr-i); |
| return -parsed; |
| } |
| pke_parsed += i; |
| |
| /* dest_node is actually the parent of this leaf, so skip the first ".." */ |
| dst_node = dest_node; |
| for (i = 1; i < dest_parent_times; ++i) { |
| dst_node = dst_node->parent; |
| if (!dst_node) { |
| if (!first) { |
| LOGVAL(LYE_NORESOLV, line, path_key_expr); |
| } |
| return 0; |
| } |
| } |
| while (1) { |
| rc = resolve_sibling(mod, dst_node->child, dest_pref, dest_pref_len, dest, dest_len, |
| LYS_CONTAINER | LYS_LIST | LYS_LEAF, &dst_node); |
| if (rc) { |
| if ((rc == -1) || !first) { |
| LOGVAL(LYE_NORESOLV, line, path_key_expr); |
| } |
| return 0; |
| } |
| |
| if (pke_len == pke_parsed) { |
| break; |
| } |
| |
| if ((i = parse_path_key_expr(path_key_expr+pke_parsed, &dest_pref, &dest_pref_len, &dest, &dest_len, |
| &dest_parent_times)) < 1) { |
| LOGVAL(LYE_INCHAR, line, (path_key_expr+pke_parsed)[-i], (path_key_expr+pke_parsed)-i); |
| return -parsed; |
| } |
| pke_parsed += i; |
| } |
| |
| /* check source - dest match */ |
| if (dst_node->nodetype != LYS_LEAF) { |
| LOGVAL(LYE_NORESOLV, line, path-parsed); |
| LOGVAL(LYE_SPEC, 0, "Destination node not a leaf, but %s.", strnodetype(dst_node->nodetype)); |
| return -parsed; |
| } |
| } while (has_predicate); |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Resolve a path (leafref) in schema context. Logs directly. |
| * |
| * @param[in] mod Module to use. |
| * @param[in] path Path to use. |
| * @param[in] parent_node Parent of the leafref. |
| * @param[in] first Whether this is the first resolution try. Affects logging. |
| * @param[in] line Line in the input file. |
| * @param[out] ret Pointer to the resolved schema node. Can be NULL. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| static int |
| resolve_path_arg_schema(struct lys_module *mod, const char *path, struct lys_node *parent_node, int first, |
| uint32_t line, struct lys_node **ret) |
| { |
| struct lys_node *node; |
| const char *id, *prefix, *name; |
| int pref_len, nam_len, parent_times, has_predicate; |
| int i, first_iter, rc; |
| |
| first_iter = 1; |
| parent_times = 0; |
| id = path; |
| |
| do { |
| if ((i = parse_path_arg(id, &prefix, &pref_len, &name, &nam_len, &parent_times, &has_predicate)) < 1) { |
| LOGVAL(LYE_INCHAR, line, id[-i], &id[-i]); |
| return -1; |
| } |
| id += i; |
| |
| if (first_iter) { |
| if (parent_times == -1) { |
| node = mod->data; |
| if (!node) { |
| if (!first) { |
| LOGVAL(LYE_NORESOLV, line, path); |
| } |
| return EXIT_FAILURE; |
| } |
| } else if (parent_times > 0) { |
| /* node is the parent already, skip one ".." */ |
| node = parent_node; |
| i = 0; |
| while (1) { |
| if (!node) { |
| if (!first) { |
| LOGVAL(LYE_NORESOLV, line, path); |
| } |
| return EXIT_FAILURE; |
| } |
| |
| /* this node is a wrong node, we actually need the augment target */ |
| if (node->nodetype == LYS_AUGMENT) { |
| node = ((struct lys_node_augment *)node)->target; |
| if (!node) { |
| continue; |
| } |
| } |
| |
| ++i; |
| if (i == parent_times) { |
| break; |
| } |
| node = node->parent; |
| } |
| node = node->child; |
| } else { |
| LOGINT; |
| return -1; |
| } |
| first_iter = 0; |
| } else { |
| node = node->child; |
| } |
| |
| rc = resolve_sibling(mod, node, prefix, pref_len, name, nam_len, LYS_ANY & (~LYS_USES), &node); |
| if (rc) { |
| if ((rc == -1) || !first) { |
| LOGVAL(LYE_NORESOLV, line, path); |
| } |
| return rc; |
| } |
| |
| if (has_predicate) { |
| /* we have predicate, so the current result must be list */ |
| if (node->nodetype != LYS_LIST) { |
| LOGVAL(LYE_NORESOLV, line, path); |
| return -1; |
| } |
| |
| i = resolve_path_predicate_schema(id, mod, node, parent_node, first, line); |
| if (!i) { |
| return EXIT_FAILURE; |
| } else if (i < 0) { |
| return -1; |
| } |
| id += i; |
| } |
| } while (id[0]); |
| |
| /* the target must be leaf or leaf-list */ |
| if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) { |
| LOGVAL(LYE_NORESOLV, line, path); |
| return -1; |
| } |
| |
| if (ret) { |
| *ret = node; |
| } |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Resolve instance-identifier predicate. Does not log. |
| * |
| * @param[in] pred Predicate to use. |
| * @param[in,out] node_match Nodes matching the restriction without |
| * the predicate. Nodes not satisfying |
| * the predicate are removed. |
| * |
| * @return Number of characters successfully parsed, |
| * positive on success, negative on failure. |
| */ |
| static int |
| resolve_predicate_json(const char *pred, struct unres_data *node_match) |
| { |
| /* ... /node[target = value] ... */ |
| struct unres_data target_match; |
| struct ly_ctx *ctx; |
| struct lys_module *mod; |
| const char *model, *name, *value; |
| char *str; |
| int mod_len, nam_len, val_len, i, has_predicate, cur_idx, idx, parsed; |
| uint32_t j; |
| |
| assert(pred && node_match->count); |
| |
| ctx = node_match->node[0]->schema->module->ctx; |
| idx = -1; |
| parsed = 0; |
| |
| do { |
| if ((i = parse_predicate_json(pred, &model, &mod_len, &name, &nam_len, &value, &val_len, &has_predicate)) < 1) { |
| return -parsed+i; |
| } |
| parsed += i; |
| pred += i; |
| |
| /* pos */ |
| if (isdigit(name[0])) { |
| idx = atoi(name); |
| } |
| |
| for (cur_idx = 0, j = 0; j < node_match->count; ++cur_idx) { |
| /* target */ |
| memset(&target_match, 0, sizeof target_match); |
| if ((name[0] == '.') || !value) { |
| target_match.count = 1; |
| target_match.node = malloc(sizeof *target_match.node); |
| target_match.node[0] = node_match->node[j]; |
| } else { |
| str = strndup(model, mod_len); |
| mod = ly_ctx_get_module(ctx, str, NULL); |
| free(str); |
| |
| if (resolve_data(mod, name, nam_len, node_match->node[j], &target_match)) { |
| goto remove_instid; |
| } |
| } |
| |
| /* check that we have the correct type */ |
| if (name[0] == '.') { |
| if (node_match->node[j]->schema->nodetype != LYS_LEAFLIST) { |
| goto remove_instid; |
| } |
| } else if (value) { |
| if (node_match->node[j]->schema->nodetype != LYS_LIST) { |
| goto remove_instid; |
| } |
| } |
| |
| if ((value && (strncmp(((struct lyd_node_leaf_list *)target_match.node[0])->value_str, value, val_len) |
| || ((struct lyd_node_leaf_list *)target_match.node[0])->value_str[val_len])) |
| || (!value && (idx != cur_idx))) { |
| goto remove_instid; |
| } |
| |
| free(target_match.node); |
| |
| /* leafref is ok, continue check with next leafref */ |
| ++j; |
| continue; |
| |
| remove_instid: |
| free(target_match.node); |
| |
| /* does not fulfill conditions, remove leafref record */ |
| unres_data_del(node_match, j); |
| } |
| } while (has_predicate); |
| |
| return parsed; |
| } |
| |
| /** |
| * @brief Resolve instance-identifier. Logs directly. |
| * |
| * @param[in] data Any node in the data tree, used to get a data tree root and context |
| * @param[in] path Instance-identifier node value. |
| * @param[in] line Source line for error messages. |
| * |
| * @return Matching node or NULL if no such a node exists. If error occurs, NULL is returned and ly_errno is set. |
| */ |
| static struct lyd_node * |
| resolve_instid_json(struct lyd_node *data, const char *path, int line) |
| { |
| int i = 0, j; |
| struct lyd_node *result = NULL; |
| struct lys_module *mod = NULL; |
| struct ly_ctx *ctx = data->schema->module->ctx; |
| const char *model, *name; |
| char *str; |
| int mod_len, name_len, has_predicate; |
| struct unres_data node_match; |
| uint32_t k; |
| |
| memset(&node_match, 0, sizeof node_match); |
| |
| /* we need root to resolve absolute path */ |
| for (; data->parent; data = data->parent); |
| /* we're still parsing it and the pointer is not correct yet */ |
| if (data->prev) { |
| for (; data->prev->next; data = data->prev); |
| } |
| |
| /* search for the instance node */ |
| while (path[i]) { |
| j = parse_instance_identifier_json(&path[i], &model, &mod_len, &name, &name_len, &has_predicate); |
| if (j <= 0) { |
| LOGVAL(LYE_INCHAR, line, path[i-j], &path[i-j]); |
| goto error; |
| } |
| i += j; |
| |
| str = strndup(model, mod_len); |
| mod = ly_ctx_get_module(ctx, str, NULL); |
| free(str); |
| |
| if (!mod) { |
| /* no instance exists */ |
| return NULL; |
| } |
| |
| if (resolve_data(mod, name, name_len, data, &node_match)) { |
| /* no instance exists */ |
| return NULL; |
| } |
| |
| if (has_predicate) { |
| /* we have predicate, so the current results must be list or leaf-list */ |
| for (k = 0; k < node_match.count;) { |
| if ((node_match.node[k]->schema->nodetype == LYS_LIST && |
| ((struct lys_node_list *)node_match.node[k]->schema)->keys) |
| || (node_match.node[k]->schema->nodetype == LYS_LEAFLIST)) { |
| /* instid is ok, continue check with next instid */ |
| ++k; |
| continue; |
| } |
| |
| /* does not fulfill conditions, remove inst record */ |
| unres_data_del(&node_match, k); |
| } |
| |
| j = resolve_predicate_json(&path[i], &node_match); |
| if (j < 1) { |
| LOGVAL(LYE_INPRED, line, &path[i-j]); |
| goto error; |
| } |
| i += j; |
| |
| if (!node_match.count) { |
| /* no instance exists */ |
| return NULL; |
| } |
| } |
| } |
| |
| if (!node_match.count) { |
| /* no instance exists */ |
| return NULL; |
| } else if (node_match.count > 1) { |
| /* instance identifier must resolve to a single node */ |
| LOGVAL(LYE_TOOMANY, line, path, "data tree"); |
| |
| /* cleanup */ |
| free(node_match.node); |
| |
| return NULL; |
| } else { |
| /* we have required result, remember it and cleanup */ |
| result = node_match.node[0]; |
| free(node_match.node); |
| |
| return result; |
| } |
| |
| error: |
| |
| /* cleanup */ |
| free(node_match.node); |
| |
| return NULL; |
| } |
| |
| /** |
| * @brief Passes config flag down to children. Does not log. |
| * |
| * @param[in] node Parent node. |
| */ |
| static void |
| inherit_config_flag(struct lys_node *node) |
| { |
| LY_TREE_FOR(node, node) { |
| node->flags |= node->parent->flags & LYS_CONFIG_MASK; |
| inherit_config_flag(node->child); |
| } |
| } |
| |
| /** |
| * @brief Resolve augment target. Does not log. |
| * |
| * @param[in] aug Augment to use. |
| * @param[in] siblings Nodes where to start the search in. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| int |
| resolve_augment(struct lys_node_augment *aug, struct lys_node *siblings) |
| { |
| int rc; |
| struct lys_node *sub; |
| |
| assert(aug); |
| |
| /* resolve target node */ |
| rc = resolve_schema_nodeid(aug->target_name, siblings, aug->module, LYS_AUGMENT, &aug->target); |
| if (rc) { |
| return rc; |
| } |
| |
| if (!aug->child) { |
| /* nothing to do */ |
| LOGWRN("Augment \"%s\" without children.", aug->target_name); |
| return EXIT_SUCCESS; |
| } |
| |
| /* inherit config information from parent, augment does not have |
| * config property, but we need to keep the information for subelements |
| */ |
| aug->flags |= aug->target->flags & LYS_CONFIG_MASK; |
| LY_TREE_FOR(aug->child, sub) { |
| inherit_config_flag(sub); |
| } |
| |
| /* check identifier uniquness as in lys_node_addchild() */ |
| LY_TREE_FOR(aug->child, sub) { |
| if (lys_check_id(sub, aug->parent, aug->module)) { |
| return -1; |
| } |
| } |
| /* reconnect augmenting data into the target - add them to the target child list */ |
| if (aug->target->child) { |
| sub = aug->target->child->prev; /* remember current target's last node */ |
| sub->next = aug->child; /* connect augmenting data after target's last node */ |
| aug->target->child->prev = aug->child->prev; /* new target's last node is last augmenting node */ |
| aug->child->prev = sub; /* finish connecting of both child lists */ |
| } else { |
| aug->target->child = aug->child; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Resolve uses, apply augments, refines. Logs directly. |
| * |
| * @param[in] uses Uses to use. |
| * @param[in,out] unres List of unresolved items. |
| * @param[in] line Line in the input file. |
| * |
| * @return EXIT_SUCCESS on success, -1 on error. |
| */ |
| static int |
| resolve_uses(struct lys_node_uses *uses, struct unres_schema *unres, uint32_t line) |
| { |
| struct ly_ctx *ctx; |
| struct lys_node *node = NULL, *node_aux; |
| struct lys_refine *rfn; |
| struct lys_restr *must, **old_must; |
| int i, j, rc; |
| uint8_t size, *old_size; |
| |
| assert(uses->grp); |
| /* HACK just check that the grouing is resolved */ |
| assert(!uses->grp->nacm); |
| |
| /* copy the data nodes from grouping into the uses context */ |
| LY_TREE_FOR(uses->grp->child, node) { |
| node_aux = lys_node_dup(uses->module, node, uses->flags, uses->nacm, 1, unres); |
| if (!node_aux) { |
| LOGVAL(LYE_SPEC, line, "Copying data from grouping failed."); |
| return -1; |
| } |
| if (lys_node_addchild((struct lys_node *)uses, NULL, node_aux)) { |
| /* error logged */ |
| lys_node_free(node_aux); |
| return -1; |
| } |
| } |
| ctx = uses->module->ctx; |
| |
| /* we managed to copy the grouping, the rest must be possible to resolve */ |
| |
| /* apply refines */ |
| for (i = 0; i < uses->refine_size; i++) { |
| rfn = &uses->refine[i]; |
| rc = resolve_schema_nodeid(rfn->target_name, uses->child, uses->module, LYS_LEAF, &node); |
| if (rc) { |
| LOGVAL(LYE_INARG, line, rfn->target_name, "refine"); |
| return -1; |
| } |
| |
| if (rfn->target_type && !(node->nodetype & rfn->target_type)) { |
| LOGVAL(LYE_SPEC, line, "Refine substatements not applicable to the target-node."); |
| return -1; |
| } |
| |
| /* description on any nodetype */ |
| if (rfn->dsc) { |
| lydict_remove(ctx, node->dsc); |
| node->dsc = lydict_insert(ctx, rfn->dsc, 0); |
| } |
| |
| /* reference on any nodetype */ |
| if (rfn->ref) { |
| lydict_remove(ctx, node->ref); |
| node->ref = lydict_insert(ctx, rfn->ref, 0); |
| } |
| |
| /* config on any nodetype */ |
| if (rfn->flags & LYS_CONFIG_MASK) { |
| node->flags &= ~LYS_CONFIG_MASK; |
| node->flags |= (rfn->flags & LYS_CONFIG_MASK); |
| } |
| |
| /* default value ... */ |
| if (rfn->mod.dflt) { |
| if (node->nodetype == LYS_LEAF) { |
| /* leaf */ |
| lydict_remove(ctx, ((struct lys_node_leaf *)node)->dflt); |
| ((struct lys_node_leaf *)node)->dflt = lydict_insert(ctx, rfn->mod.dflt, 0); |
| } else if (node->nodetype == LYS_CHOICE) { |
| /* choice */ |
| rc = resolve_schema_nodeid(rfn->mod.dflt, node->child, node->module, LYS_CHOICE, &((struct lys_node_choice *)node)->dflt); |
| if (rc) { |
| LOGVAL(LYE_INARG, line, rfn->mod.dflt, "default"); |
| return -1; |
| } |
| } |
| } |
| |
| /* mandatory on leaf, anyxml or choice */ |
| if (rfn->flags & LYS_MAND_MASK) { |
| if (node->nodetype & (LYS_LEAF | LYS_ANYXML | LYS_CHOICE)) { |
| /* remove current value */ |
| node->flags &= ~LYS_MAND_MASK; |
| |
| /* set new value */ |
| node->flags |= (rfn->flags & LYS_MAND_MASK); |
| } |
| } |
| |
| /* presence on container */ |
| if ((node->nodetype & LYS_CONTAINER) && rfn->mod.presence) { |
| lydict_remove(ctx, ((struct lys_node_container *)node)->presence); |
| ((struct lys_node_container *)node)->presence = lydict_insert(ctx, rfn->mod.presence, 0); |
| } |
| |
| /* min/max-elements on list or leaf-list */ |
| /* magic - bit 3 in flags means min set, bit 4 says max set */ |
| if (node->nodetype == LYS_LIST) { |
| if (rfn->flags & 0x04) { |
| ((struct lys_node_list *)node)->min = rfn->mod.list.min; |
| } |
| if (rfn->flags & 0x08) { |
| ((struct lys_node_list *)node)->max = rfn->mod.list.max; |
| } |
| } else if (node->nodetype == LYS_LEAFLIST) { |
| if (rfn->flags & 0x04) { |
| ((struct lys_node_leaflist *)node)->min = rfn->mod.list.min; |
| } |
| if (rfn->flags & 0x08) { |
| ((struct lys_node_leaflist *)node)->max = rfn->mod.list.max; |
| } |
| } |
| |
| /* must in leaf, leaf-list, list, container or anyxml */ |
| if (rfn->must_size) { |
| switch (node->nodetype) { |
| case LYS_LEAF: |
| old_size = &((struct lys_node_leaf *)node)->must_size; |
| old_must = &((struct lys_node_leaf *)node)->must; |
| break; |
| case LYS_LEAFLIST: |
| old_size = &((struct lys_node_leaflist *)node)->must_size; |
| old_must = &((struct lys_node_leaflist *)node)->must; |
| break; |
| case LYS_LIST: |
| old_size = &((struct lys_node_list *)node)->must_size; |
| old_must = &((struct lys_node_list *)node)->must; |
| break; |
| case LYS_CONTAINER: |
| old_size = &((struct lys_node_container *)node)->must_size; |
| old_must = &((struct lys_node_container *)node)->must; |
| break; |
| case LYS_ANYXML: |
| old_size = &((struct lys_node_anyxml *)node)->must_size; |
| old_must = &((struct lys_node_anyxml *)node)->must; |
| break; |
| default: |
| LOGINT; |
| return -1; |
| } |
| |
| size = *old_size + rfn->must_size; |
| must = realloc(*old_must, size * sizeof *rfn->must); |
| if (!must) { |
| LOGMEM; |
| return -1; |
| } |
| for (i = 0, j = *old_size; i < rfn->must_size; i++, j++) { |
| must[j].expr = lydict_insert(ctx, rfn->must[i].expr, 0); |
| must[j].dsc = lydict_insert(ctx, rfn->must[i].dsc, 0); |
| must[j].ref = lydict_insert(ctx, rfn->must[i].ref, 0); |
| must[j].eapptag = lydict_insert(ctx, rfn->must[i].eapptag, 0); |
| must[j].emsg = lydict_insert(ctx, rfn->must[i].emsg, 0); |
| } |
| |
| *old_must = must; |
| *old_size = size; |
| } |
| } |
| |
| /* apply augments */ |
| for (i = 0; i < uses->augment_size; i++) { |
| rc = resolve_augment(&uses->augment[i], uses->child); |
| if (rc) { |
| LOGVAL(LYE_INRESOLV, line, "augment", uses->augment[i].target_name); |
| return -1; |
| } |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Resolve base identity recursively. Does not log. |
| * |
| * @param[in] module Main module. |
| * @param[in] ident Identity to use. |
| * @param[in] basename Base name of the identity. |
| * @param[out] ret Pointer to the resolved identity. Can be NULL. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference. |
| */ |
| static int |
| resolve_base_ident_sub(struct lys_module *module, struct lys_ident *ident, const char *basename, |
| struct lys_ident **ret) |
| { |
| uint32_t i, j; |
| struct lys_ident *base_iter = NULL; |
| struct lys_ident_der *der; |
| |
| /* search module */ |
| for (i = 0; i < module->ident_size; i++) { |
| if (!strcmp(basename, module->ident[i].name)) { |
| |
| if (!ident) { |
| /* just search for type, so do not modify anything, just return |
| * the base identity pointer |
| */ |
| if (ret) { |
| *ret = &module->ident[i]; |
| } |
| return EXIT_SUCCESS; |
| } |
| |
| /* we are resolving identity definition, so now update structures */ |
| ident->base = base_iter = &module->ident[i]; |
| |
| break; |
| } |
| } |
| |
| /* search submodules */ |
| if (!base_iter) { |
| for (j = 0; j < module->inc_size; j++) { |
| for (i = 0; i < module->inc[j].submodule->ident_size; i++) { |
| if (!strcmp(basename, module->inc[j].submodule->ident[i].name)) { |
| |
| if (!ident) { |
| if (ret) { |
| *ret = &module->inc[j].submodule->ident[i]; |
| } |
| return EXIT_SUCCESS; |
| } |
| |
| ident->base = base_iter = &module->inc[j].submodule->ident[i]; |
| break; |
| } |
| } |
| } |
| } |
| |
| /* we found it somewhere */ |
| if (base_iter) { |
| while (base_iter) { |
| for (der = base_iter->der; der && der->next; der = der->next); |
| if (der) { |
| der->next = malloc(sizeof *der); |
| der = der->next; |
| } else { |
| ident->base->der = der = malloc(sizeof *der); |
| } |
| der->next = NULL; |
| der->ident = ident; |
| |
| base_iter = base_iter->base; |
| } |
| if (ret) { |
| *ret = ident->base; |
| } |
| return EXIT_SUCCESS; |
| } |
| |
| return EXIT_FAILURE; |
| } |
| |
| /** |
| * @brief Resolve base identity. Logs directly. |
| * |
| * @param[in] module Main module. |
| * @param[in] ident Identity to use. |
| * @param[in] basename Base name of the identity. |
| * @param[in] parent Either "type" or "ident". |
| * @param[in] first Whether this is the first resolution try. Affects logging. |
| * @param[in] line Line in the input file. |
| * @param[out] ret Pointer to the resolved identity. Can be NULL. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| static int |
| resolve_base_ident(struct lys_module *module, struct lys_ident *ident, const char *basename, const char* parent, |
| int first, uint32_t line, struct lys_ident **ret) |
| { |
| const char *name; |
| int i, prefix_len = 0; |
| |
| /* search for the base identity */ |
| name = strchr(basename, ':'); |
| if (name) { |
| /* set name to correct position after colon */ |
| prefix_len = name - basename; |
| name++; |
| |
| if (!strncmp(basename, module->prefix, prefix_len) && !module->prefix[prefix_len]) { |
| /* prefix refers to the current module, ignore it */ |
| prefix_len = 0; |
| } |
| } else { |
| name = basename; |
| } |
| |
| if (prefix_len) { |
| /* get module where to search */ |
| module = resolve_prefixed_module(module, basename, prefix_len); |
| if (!module) { |
| /* identity refers unknown data model */ |
| LOGVAL(LYE_INPREF, line, basename); |
| return -1; |
| } |
| } else { |
| /* search in submodules */ |
| for (i = 0; i < module->inc_size; i++) { |
| if (!resolve_base_ident_sub((struct lys_module *)module->inc[i].submodule, ident, name, ret)) { |
| return EXIT_SUCCESS; |
| } |
| } |
| } |
| |
| /* search in the identified module */ |
| if (!resolve_base_ident_sub(module, ident, name, ret)) { |
| return EXIT_SUCCESS; |
| } |
| |
| if (!first) { |
| LOGVAL(LYE_INARG, line, basename, parent); |
| } |
| return EXIT_FAILURE; |
| } |
| |
| /** |
| * @brief Resolve JSON format identityref. Logs directly. |
| * |
| * @param[in] base Base identity. |
| * @param[in] ident_name Identityref name. |
| * @param[in] line Line from the input file. |
| * |
| * @return Pointer to the identity resolvent, NULL on error. |
| */ |
| struct lys_ident * |
| resolve_identref_json(struct lys_ident *base, const char *ident_name, uint32_t line) |
| { |
| const char *mod_name, *name; |
| int mod_name_len, rc; |
| struct lys_ident_der *der; |
| |
| if (!base || !ident_name) { |
| return NULL; |
| } |
| |
| rc = parse_node_identifier(ident_name, &mod_name, &mod_name_len, &name, NULL); |
| if (rc < (signed)strlen(ident_name)) { |
| LOGVAL(LYE_INCHAR, line, ident_name[-rc], &ident_name[-rc]); |
| return NULL; |
| } |
| |
| if (!strcmp(base->name, name) && (!mod_name |
| || (!strncmp(base->module->name, mod_name, mod_name_len) && !base->module->name[mod_name_len]))) { |
| return base; |
| } |
| |
| for (der = base->der; der; der = der->next) { |
| if (!strcmp(der->ident->name, name) && (!mod_name |
| || (!strncmp(der->ident->module->name, mod_name, mod_name_len) |
| && !der->ident->module->name[mod_name_len]))) { |
| /* we have match */ |
| return der->ident; |
| } |
| } |
| |
| LOGVAL(LYE_INRESOLV, line, "identityref", ident_name); |
| return NULL; |
| } |
| |
| /** |
| * @brief Resolve (find) choice default case. Does not log. |
| * |
| * @param[in] choic Choice to use. |
| * @param[in] dflt Name of the default case. |
| * |
| * @return Pointer to the default node or NULL. |
| */ |
| static struct lys_node * |
| resolve_choice_dflt(struct lys_node_choice *choic, const char *dflt) |
| { |
| struct lys_node *child, *ret; |
| |
| LY_TREE_FOR(choic->child, child) { |
| if (child->nodetype == LYS_USES) { |
| ret = resolve_choice_dflt((struct lys_node_choice *)child, dflt); |
| if (ret) { |
| return ret; |
| } |
| } |
| |
| if ((child->name == dflt) && (child->nodetype & (LYS_ANYXML | LYS_CASE |
| | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST))) { |
| return child; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * @brief Resolve unresolved uses. Logs directly. |
| * |
| * @param[in] uses Uses to use. |
| * @param[in] unres Specific unres item. |
| * @param[in] first Whether this is the first resolution try. |
| * @param[in] line Line in the input file. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| static int |
| resolve_unres_schema_uses(struct lys_node_uses *uses, struct unres_schema *unres, int first, uint32_t line) |
| { |
| int rc; |
| struct lys_node *parent; |
| |
| /* HACK change unres uses count if it's in a grouping (nacm field used for it) */ |
| for (parent = uses->parent; parent && (parent->nodetype != LYS_GROUPING); parent = parent->parent); |
| |
| if (!uses->grp) { |
| rc = resolve_grouping(uses, first, line); |
| if (rc) { |
| if (parent && first && (rc == EXIT_FAILURE)) { |
| ++parent->nacm; |
| } |
| return rc; |
| } |
| } |
| |
| if (uses->grp->nacm) { |
| if (parent && first) { |
| ++parent->nacm; |
| } |
| return EXIT_FAILURE; |
| } |
| |
| rc = resolve_uses(uses, unres, line); |
| if (!rc) { |
| /* decrease unres count only if not first try */ |
| if (parent && !first) { |
| if (!parent->nacm) { |
| LOGINT; |
| return -1; |
| } |
| --parent->nacm; |
| } |
| return EXIT_SUCCESS; |
| } |
| |
| if (parent && first && (rc == EXIT_FAILURE)) { |
| ++parent->nacm; |
| } |
| return rc; |
| } |
| |
| /** |
| * @brief Resolve list keys. Logs directly. |
| * |
| * @param[in] list List to use. |
| * @param[in] keys_str Keys node value. |
| * @param[in] first Whether this is the first resolution try. Affects logging. |
| * @param[in] line Line in the input file. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| static int |
| resolve_list_keys(struct lys_module *mod, struct lys_node_list *list, const char *keys_str, int first, uint32_t line) |
| { |
| int i, len, rc; |
| const char *value; |
| |
| for (i = 0; i < list->keys_size; ++i) { |
| /* get the key name */ |
| if ((value = strpbrk(keys_str, " \t\n"))) { |
| len = value - keys_str; |
| while (isspace(value[0])) { |
| value++; |
| } |
| } else { |
| len = strlen(keys_str); |
| } |
| |
| rc = resolve_sibling(mod, list->child, NULL, 0, keys_str, len, LYS_LEAF, (struct lys_node **)&list->keys[i]); |
| if (rc) { |
| if ((rc == -1) || !first) { |
| LOGVAL(LYE_INRESOLV, line, "list keys", keys_str); |
| } |
| return rc; |
| } |
| |
| if (check_key(list->keys[i], list->flags, list->keys, i, keys_str, len, line)) { |
| /* check_key logs */ |
| return -1; |
| } |
| |
| /* prepare for next iteration */ |
| while (value && isspace(value[0])) { |
| value++; |
| } |
| keys_str = value; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Resolve (check) all must conditions of \p node. |
| * Logs directly. |
| * |
| * @param[in] node Data node with optional must statements. |
| * @param[in] first Whether this is the first resolution to try. |
| * @param[in] line Line in the input file. |
| * |
| * @return EXIT_SUCCESS on pass, EXIT_FAILURE on fail, -1 on error. |
| */ |
| static int |
| resolve_must(struct lyd_node *node, int first, uint32_t line) |
| { |
| uint8_t i, must_size; |
| struct lys_restr *must; |
| struct lyxp_set set; |
| |
| assert(node); |
| memset(&set, 0, sizeof set); |
| |
| switch (node->schema->nodetype) { |
| case LYS_CONTAINER: |
| must_size = ((struct lys_node_container *)node->schema)->must_size; |
| must = ((struct lys_node_container *)node->schema)->must; |
| break; |
| case LYS_LEAF: |
| must_size = ((struct lys_node_leaf *)node->schema)->must_size; |
| must = ((struct lys_node_leaf *)node->schema)->must; |
| break; |
| case LYS_LEAFLIST: |
| must_size = ((struct lys_node_leaflist *)node->schema)->must_size; |
| must = ((struct lys_node_leaflist *)node->schema)->must; |
| break; |
| case LYS_LIST: |
| must_size = ((struct lys_node_list *)node->schema)->must_size; |
| must = ((struct lys_node_list *)node->schema)->must; |
| break; |
| case LYS_ANYXML: |
| must_size = ((struct lys_node_anyxml *)node->schema)->must_size; |
| must = ((struct lys_node_anyxml *)node->schema)->must; |
| break; |
| default: |
| must_size = 0; |
| break; |
| } |
| |
| for (i = 0; i < must_size; ++i) { |
| if (lyxp_eval(must[i].expr, node, &set, line)) { |
| return -1; |
| } |
| |
| lyxp_set_cast(&set, LYXP_SET_BOOLEAN, node); |
| |
| if (!set.value.bool) { |
| if (!first) { |
| LOGVAL(LYE_NOCOND, line, "Must", must[i].expr); |
| } |
| return 1; |
| } |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Resolve (find) when condition context node. Does not log. |
| * |
| * @param[in] node Data node, whose conditional definition is being decided. |
| * @param[in] schema Schema node with a when condition. |
| * |
| * @return Context node. |
| */ |
| static struct lyd_node * |
| resolve_when_ctx_node(struct lyd_node *node, struct lys_node *schema) |
| { |
| struct lyd_node *parent; |
| struct lys_node *sparent; |
| uint16_t i, data_depth, schema_depth; |
| |
| /* find a not schema-only node */ |
| while (schema->nodetype & (LYS_USES | LYS_CHOICE | LYS_CASE | LYS_AUGMENT | LYS_INPUT | LYS_OUTPUT)) { |
| schema = lys_parent(schema); |
| if (!schema) { |
| return NULL; |
| } |
| } |
| |
| /* get node depths */ |
| for (parent = node, data_depth = 0; parent; parent = parent->parent, ++data_depth); |
| for (sparent = lys_parent(schema), schema_depth = 1; sparent; sparent = lys_parent(sparent)) { |
| if (sparent->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYXML | LYS_NOTIF | LYS_RPC)) { |
| ++schema_depth; |
| } |
| } |
| if (data_depth < schema_depth) { |
| return NULL; |
| } |
| |
| /* find the corresponding data node */ |
| for (i = 0; i < data_depth - schema_depth; ++i) { |
| node = node->parent; |
| } |
| if (node->schema != schema) { |
| return NULL; |
| } |
| |
| return node; |
| } |
| |
| /** |
| * @brief Resolve (check) all when conditions relevant for \p node. |
| * Logs directly. |
| * |
| * @param[in] node Data node, whose conditional reference, if such, is being decided. |
| * @param[in] first Whether this is the first resolution to try. |
| * @param[in] line Line in the input file. |
| * |
| * @return EXIT_SUCCESS on pass, EXIT_FAILURE on fail, -1 on error. |
| */ |
| static int |
| resolve_when(struct lyd_node *node, int first, uint32_t line) |
| { |
| struct lyd_node *ctx_node = NULL; |
| struct lys_node *parent; |
| struct lyxp_set set; |
| |
| assert(node); |
| memset(&set, 0, sizeof set); |
| |
| if (!(node->schema->nodetype & (LYS_NOTIF | LYS_RPC)) && (((struct lys_node_container *)node->schema)->when)) { |
| if (lyxp_eval(((struct lys_node_container *)node->schema)->when->cond, node, &set, line)) { |
| return -1; |
| } |
| |
| lyxp_set_cast(&set, LYXP_SET_BOOLEAN, node); |
| |
| if (!set.value.bool) { |
| if (!first) { |
| LOGVAL(LYE_NOCOND, line, "When", ((struct lys_node_container *)node->schema)->when->cond); |
| } |
| return 1; |
| } |
| } |
| |
| parent = node->schema; |
| goto check_augment; |
| |
| /* check when in every schema node that affects node */ |
| while (parent && (parent->nodetype & (LYS_USES | LYS_CHOICE | LYS_CASE))) { |
| if (((struct lys_node_uses *)parent)->when) { |
| if (!ctx_node) { |
| ctx_node = resolve_when_ctx_node(node, parent); |
| if (!ctx_node) { |
| LOGINT; |
| return -1; |
| } |
| } |
| if (lyxp_eval(((struct lys_node_uses *)parent)->when->cond, ctx_node, &set, line)) { |
| return -1; |
| } |
| |
| lyxp_set_cast(&set, LYXP_SET_BOOLEAN, ctx_node); |
| |
| if (!set.value.bool) { |
| if (!first) { |
| LOGVAL(LYE_NOCOND, line, "When", ((struct lys_node_uses *)parent)->when->cond); |
| } |
| return 1; |
| } |
| } |
| |
| check_augment: |
| if ((parent->parent && (parent->parent->nodetype == LYS_AUGMENT) && (((struct lys_node_augment *)parent->parent)->when))) { |
| if (!ctx_node) { |
| ctx_node = resolve_when_ctx_node(node, parent->parent); |
| if (!ctx_node) { |
| LOGINT; |
| return -1; |
| } |
| } |
| if (lyxp_eval(((struct lys_node_augment *)parent->parent)->when->cond, ctx_node, &set, line)) { |
| return -1; |
| } |
| |
| lyxp_set_cast(&set, LYXP_SET_BOOLEAN, ctx_node); |
| |
| if (!set.value.bool) { |
| if (!first) { |
| LOGVAL(LYE_NOCOND, line, "When", ((struct lys_node_augment *)parent->parent)->when->cond); |
| } |
| return 1; |
| } |
| } |
| |
| parent = lys_parent(parent); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Resolve a single unres schema item. Logs indirectly. |
| * |
| * @param[in] mod Main module. |
| * @param[in] item Item to resolve. Type determined by \p type. |
| * @param[in] type Type of the unresolved item. |
| * @param[in] str_snode String, a schema node, or NULL. |
| * @param[in] unres Unres schema structure to use. |
| * @param[in] first Whether this is the first resolution try. |
| * @param[in] line Line in the input file. 0 skips line print. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| static int |
| resolve_unres_schema_item(struct lys_module *mod, void *item, enum UNRES_ITEM type, void *str_snode, |
| struct unres_schema *unres, int first, uint32_t line) |
| { |
| int rc = -1, has_str = 0; |
| struct lys_node *node; |
| const char *base_name; |
| |
| struct lys_ident *ident; |
| struct lys_type *stype; |
| struct lys_feature **feat_ptr; |
| struct lys_node_choice *choic; |
| struct lys_unique *uniq; |
| |
| switch (type) { |
| case UNRES_IDENT: |
| base_name = str_snode; |
| ident = item; |
| |
| rc = resolve_base_ident(mod, ident, base_name, "ident", first, line, NULL); |
| has_str = 1; |
| break; |
| case UNRES_TYPE_IDENTREF: |
| base_name = str_snode; |
| stype = item; |
| |
| rc = resolve_base_ident(mod, NULL, base_name, "type", first, line, &stype->info.ident.ref); |
| has_str = 1; |
| break; |
| case UNRES_TYPE_LEAFREF: |
| node = str_snode; |
| stype = item; |
| |
| rc = resolve_path_arg_schema(mod, stype->info.lref.path, node, first, line, |
| (struct lys_node **)&stype->info.lref.target); |
| has_str = 0; |
| break; |
| case UNRES_TYPE_DER: |
| base_name = str_snode; |
| stype = item; |
| |
| /* HACK type->der is temporarily its parent */ |
| rc = resolve_superior_type(base_name, stype->prefix, mod, (struct lys_node *)stype->der, &stype->der); |
| if (rc == -1) { |
| LOGVAL(LYE_INPREF, line, stype->prefix); |
| } else if (!rc) { |
| stype->base = stype->der->type.base; |
| } |
| has_str = 1; |
| break; |
| case UNRES_IFFEAT: |
| base_name = str_snode; |
| feat_ptr = item; |
| |
| rc = resolve_feature(base_name, mod, first, line, feat_ptr); |
| has_str = 1; |
| break; |
| case UNRES_USES: |
| rc = resolve_unres_schema_uses(item, unres, first, line); |
| has_str = 0; |
| break; |
| case UNRES_TYPE_DFLT: |
| base_name = str_snode; |
| stype = item; |
| |
| rc = check_default(stype, base_name); |
| /* do not remove base_name (dflt), it's in a typedef */ |
| has_str = 0; |
| break; |
| case UNRES_CHOICE_DFLT: |
| base_name = str_snode; |
| choic = item; |
| |
| choic->dflt = resolve_choice_dflt(choic, base_name); |
| if (choic->dflt) { |
| rc = EXIT_SUCCESS; |
| } else { |
| rc = EXIT_FAILURE; |
| } |
| has_str = 1; |
| break; |
| case UNRES_LIST_KEYS: |
| rc = resolve_list_keys(mod, item, str_snode, first, line); |
| has_str = 1; |
| break; |
| case UNRES_LIST_UNIQ: |
| /* actually the unique string */ |
| base_name = str_snode; |
| uniq = item; |
| |
| rc = resolve_unique((struct lys_node *)uniq->leafs, base_name, uniq, first, line); |
| has_str = 1; |
| break; |
| default: |
| LOGINT; |
| break; |
| } |
| |
| if (has_str && !rc) { |
| lydict_remove(mod->ctx, str_snode); |
| } |
| |
| return rc; |
| } |
| |
| /* logs directly */ |
| static void |
| print_unres_schema_item_fail(void *item, enum UNRES_ITEM type, void *str_node, uint32_t line) |
| { |
| char line_str[18]; |
| |
| if (line) { |
| sprintf(line_str, " (line %u)", line); |
| } else { |
| line_str[0] = '\0'; |
| } |
| |
| switch (type) { |
| case UNRES_IDENT: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "identity", (char *)str_node, line_str); |
| break; |
| case UNRES_TYPE_IDENTREF: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "identityref", (char *)str_node, line_str); |
| break; |
| case UNRES_TYPE_LEAFREF: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "leafref", ((struct lys_type *)item)->info.lref.path, line_str); |
| break; |
| case UNRES_TYPE_DER: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "derived type", (char *)str_node, line_str); |
| break; |
| case UNRES_IFFEAT: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "if-feature", (char *)str_node, line_str); |
| break; |
| case UNRES_USES: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "uses", ((struct lys_node_uses *)item)->name, line_str); |
| break; |
| case UNRES_TYPE_DFLT: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "type default", (char *)str_node, line_str); |
| break; |
| case UNRES_CHOICE_DFLT: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "choice default", (char *)str_node, line_str); |
| break; |
| case UNRES_LIST_KEYS: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "list keys", (char *)str_node, line_str); |
| break; |
| case UNRES_LIST_UNIQ: |
| LOGVRB("Resolving %s \"%s\" failed, it will be attempted later%s.", "list unique", (char *)str_node, line_str); |
| break; |
| default: |
| LOGINT; |
| break; |
| } |
| } |
| |
| /** |
| * @brief Resolve every unres schema item in the structure. Logs directly. |
| * |
| * @param[in] mod Main module. |
| * @param[in] unres Unres schema structure to use. |
| * |
| * @return EXIT_SUCCESS on success, -1 on error. |
| */ |
| int |
| resolve_unres_schema(struct lys_module *mod, struct unres_schema *unres) |
| { |
| uint32_t i, resolved, unres_uses, res_uses; |
| int rc; |
| |
| assert(unres); |
| |
| resolved = 0; |
| |
| /* uses */ |
| do { |
| unres_uses = 0; |
| res_uses = 0; |
| |
| for (i = 0; i < unres->count; ++i) { |
| if (unres->type[i] != UNRES_USES) { |
| continue; |
| } |
| |
| ++unres_uses; |
| rc = resolve_unres_schema_item(mod, unres->item[i], unres->type[i], unres->str_snode[i], unres, 0, |
| LOGLINE_IDX(unres, i)); |
| if (!rc) { |
| unres->type[i] = UNRES_RESOLVED; |
| ++resolved; |
| ++res_uses; |
| } else if (rc == -1) { |
| return -1; |
| } |
| } |
| } while (res_uses && (res_uses < unres_uses)); |
| |
| if (res_uses < unres_uses) { |
| LOGVAL(LYE_SPEC, 0, "There are unresolved uses left."); |
| return -1; |
| } |
| |
| /* the rest */ |
| for (i = 0; i < unres->count; ++i) { |
| if (unres->type[i] == UNRES_RESOLVED) { |
| continue; |
| } |
| |
| rc = resolve_unres_schema_item(mod, unres->item[i], unres->type[i], unres->str_snode[i], unres, 0, |
| LOGLINE_IDX(unres, i)); |
| if (rc) { |
| return rc; |
| } |
| |
| unres->type[i] = UNRES_RESOLVED; |
| ++resolved; |
| } |
| |
| if (resolved < unres->count) { |
| LOGVAL(LYE_SPEC, 0, "There are unresolved schema items left."); |
| return -1; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Try to resolve an unres schema item with a string argument. Logs indirectly. |
| * |
| * @param[in] mod Main module. |
| * @param[in] unres Unres schema structure to use. |
| * @param[in] item Item to resolve. Type determined by \p type. |
| * @param[in] type Type of the unresolved item. |
| * @param[in] str String argument. |
| * @param[in] line Line in the input file. |
| * |
| * @return EXIT_SUCCESS on success or storing the item in unres, -1 on error. |
| */ |
| int |
| unres_schema_add_str(struct lys_module *mod, struct unres_schema *unres, void *item, enum UNRES_ITEM type, const char *str, |
| uint32_t line) |
| { |
| str = lydict_insert(mod->ctx, str, 0); |
| return unres_schema_add_node(mod, unres, item, type, (struct lys_node *)str, line); |
| } |
| |
| /** |
| * @brief Try to resolve an unres schema item with a schema node argument. Logs indirectly. |
| * |
| * @param[in] mod Main module. |
| * @param[in] unres Unres schema structure to use. |
| * @param[in] item Item to resolve. Type determined by \p type. |
| * @param[in] type Type of the unresolved item. |
| * @param[in] snode Schema node argument. |
| * @param[in] line Line in the input file. |
| * |
| * @return EXIT_SUCCESS on success or storing the item in unres, -1 on error. |
| */ |
| int |
| unres_schema_add_node(struct lys_module *mod, struct unres_schema *unres, void *item, enum UNRES_ITEM type, |
| struct lys_node *snode, uint32_t line) |
| { |
| int rc; |
| |
| assert(unres && item && ((type != UNRES_LEAFREF) && (type != UNRES_INSTID) && (type != UNRES_WHEN))); |
| |
| rc = resolve_unres_schema_item(mod, item, type, snode, unres, 1, line); |
| if (rc != EXIT_FAILURE) { |
| return rc; |
| } |
| |
| print_unres_schema_item_fail(item, type, snode, line); |
| |
| unres->count++; |
| unres->item = realloc(unres->item, unres->count*sizeof *unres->item); |
| unres->item[unres->count-1] = item; |
| unres->type = realloc(unres->type, unres->count*sizeof *unres->type); |
| unres->type[unres->count-1] = type; |
| unres->str_snode = realloc(unres->str_snode, unres->count*sizeof *unres->str_snode); |
| unres->str_snode[unres->count-1] = snode; |
| #ifndef NDEBUG |
| unres->line = realloc(unres->line, unres->count*sizeof *unres->line); |
| unres->line[unres->count-1] = line; |
| #endif |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Duplicate an unres schema item. Logs indirectly. |
| * |
| * @param[in] mod Main module. |
| * @param[in] unres Unres schema structure to use. |
| * @param[in] item Old item to be resolved. |
| * @param[in] type Type of the old unresolved item. |
| * @param[in] new_item New item to use in the duplicate. |
| * |
| * @return EXIT_SUCCESS on success, -1 on error. |
| */ |
| int |
| unres_schema_dup(struct lys_module *mod, struct unres_schema *unres, void *item, enum UNRES_ITEM type, void *new_item) |
| { |
| int i; |
| |
| assert(item && new_item && ((type != UNRES_LEAFREF) && (type != UNRES_INSTID) && (type != UNRES_WHEN))); |
| |
| i = unres_schema_find(unres, item, type); |
| |
| if (i == -1) { |
| return -1; |
| } |
| |
| if ((type == UNRES_TYPE_LEAFREF) || (type == UNRES_USES) || (type == UNRES_TYPE_DFLT)) { |
| if (unres_schema_add_node(mod, unres, new_item, type, unres->str_snode[i], 0) == -1) { |
| LOGINT; |
| return -1; |
| } |
| } else { |
| if (unres_schema_add_str(mod, unres, new_item, type, unres->str_snode[i], 0) == -1) { |
| LOGINT; |
| return -1; |
| } |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /* does not log */ |
| int |
| unres_schema_find(struct unres_schema *unres, void *item, enum UNRES_ITEM type) |
| { |
| uint32_t ret = -1, i; |
| |
| for (i = 0; i < unres->count; ++i) { |
| if ((unres->item[i] == item) && (unres->type[i] == type)) { |
| ret = i; |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /* logs directly */ |
| static void |
| print_unres_data_item_fail(struct lyd_node *node, enum UNRES_ITEM type, uint32_t line) |
| { |
| struct lys_node_leaf *sleaf; |
| char line_str[18]; |
| |
| if (line) { |
| sprintf(line_str, " (line %u)", line); |
| } else { |
| line_str[0] = '\0'; |
| } |
| |
| sleaf = (struct lys_node_leaf *)node->schema; |
| |
| switch (type) { |
| case UNRES_LEAFREF: |
| LOGVRB("Leafref \"%s\" could not be resolved, it will be attempted later%s.", |
| sleaf->type.info.lref.path, line_str); |
| break; |
| case UNRES_INSTID: |
| LOGVRB("Instance-identifier \"%s\" could not be resolved, it will be attempted later%s.", |
| ((struct lyd_node_leaf_list *)node)->value_str, line_str); |
| break; |
| case UNRES_WHEN: |
| LOGVRB("There was an unsatisfied when condition, evaluation will be attempted later%s.", line_str); |
| break; |
| case UNRES_MUST: |
| LOGVRB("There was an unsatisfied must condition, evaluation will be attempted later%s.", line_str); |
| break; |
| default: |
| LOGINT; |
| break; |
| } |
| } |
| |
| /** |
| * @brief Resolve a single unres data item. Logs directly. |
| * |
| * @param[in] node Data node to resolve. |
| * @param[in] first Whether this is the first resolution try. |
| * @param[in] type Type of the unresolved item. |
| * @param[in] line Line in the input file. 0 skips line print. |
| * |
| * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error. |
| */ |
| int |
| resolve_unres_data_item(struct lyd_node *node, enum UNRES_ITEM type, int first, uint32_t line) |
| { |
| uint32_t i; |
| int rc; |
| struct lyd_node_leaf_list *leaf; |
| struct lys_node_leaf *sleaf; |
| struct unres_data matches; |
| |
| memset(&matches, 0, sizeof matches); |
| leaf = (struct lyd_node_leaf_list *)node; |
| sleaf = (struct lys_node_leaf *)leaf->schema; |
| |
| switch (type) { |
| case UNRES_LEAFREF: |
| assert(sleaf->type.base == LY_TYPE_LEAFREF); |
| if ((rc = resolve_path_arg_data(node, sleaf->type.info.lref.path, first, line, &matches))) { |
| return rc; |
| } |
| |
| /* check that value matches */ |
| for (i = 0; i < matches.count; ++i) { |
| if (leaf->value_str == ((struct lyd_node_leaf_list *)matches.node[i])->value_str) { |
| leaf->value.leafref = matches.node[i]; |
| break; |
| } |
| } |
| |
| free(matches.node); |
| memset(&matches, 0, sizeof matches); |
| |
| if (!leaf->value.leafref) { |
| /* reference not found */ |
| if (!first) { |
| LOGVAL(LYE_SPEC, line, "Leafref \"%s\" value \"%s\" did not match any node value.", |
| sleaf->type.info.lref.path, leaf->value_str); |
| } |
| return EXIT_FAILURE; |
| } |
| break; |
| |
| case UNRES_INSTID: |
| assert(sleaf->type.base == LY_TYPE_INST); |
| ly_errno = 0; |
| if (!resolve_instid_json(node, leaf->value_str, line)) { |
| if (ly_errno) { |
| return -1; |
| } else if (sleaf->type.info.inst.req > -1) { |
| if (!first) { |
| LOGVAL(LYE_SPEC, line, "There is no instance of \"%s\".", leaf->value_str); |
| } |
| return EXIT_FAILURE; |
| } else { |
| LOGVRB("There is no instance of \"%s\", but is not required.", leaf->value_str); |
| } |
| } |
| break; |
| |
| case UNRES_WHEN: |
| if ((rc = resolve_when(node, first, line))) { |
| return rc; |
| } |
| break; |
| |
| case UNRES_MUST: |
| if ((rc = resolve_must(node, first, line))) { |
| return rc; |
| } |
| break; |
| |
| default: |
| LOGINT; |
| return -1; |
| } |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Try to resolve an unres data item. Logs indirectly. |
| * |
| * @param[in] unres Unres data structure to use. |
| * @param[in] node Data node to use. |
| * @param[in] line Line in the input file. |
| * |
| * @return EXIT_SUCCESS on success or storing the item in unres, -1 on error. |
| */ |
| int |
| unres_data_add(struct unres_data *unres, struct lyd_node *node, enum UNRES_ITEM type, uint32_t line) |
| { |
| int rc; |
| |
| assert(unres && node && ((type == UNRES_LEAFREF) || (type == UNRES_INSTID) || (type == UNRES_WHEN) || (type == UNRES_MUST))); |
| |
| rc = resolve_unres_data_item(node, type, 1, line); |
| if (rc != EXIT_FAILURE) { |
| return rc; |
| } |
| |
| print_unres_data_item_fail(node, type, line); |
| |
| ++unres->count; |
| unres->node = realloc(unres->node, unres->count * sizeof *unres->node); |
| unres->node[unres->count - 1] = node; |
| unres->type = realloc(unres->type, unres->count * sizeof *unres->type); |
| unres->type[unres->count - 1] = type; |
| #ifndef NDEBUG |
| unres->line = realloc(unres->line, unres->count * sizeof *unres->line); |
| unres->line[unres->count - 1] = line; |
| #endif |
| |
| return EXIT_SUCCESS; |
| } |
| |
| /** |
| * @brief Resolve every unres data item in the structure. Logs directly. |
| * |
| * @param[in] unres Unres data structure to use. |
| * |
| * @return EXIT_SUCCESS on success, -1 on error. |
| */ |
| int |
| resolve_unres_data(struct unres_data *unres) |
| { |
| uint32_t i; |
| int rc; |
| |
| for (i = 0; i < unres->count; ++i) { |
| rc = resolve_unres_data_item(unres->node[i], unres->type[i], 0, LOGLINE_IDX(unres, i)); |
| if (rc) { |
| LOGVAL(LYE_SPEC, 0, "There are unresolved data items left."); |
| return -1; |
| } |
| } |
| |
| return EXIT_SUCCESS; |
| } |