#define _GNU_SOURCE
#include "plugins_types.h"
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"
#include "context.h"
#include "dict.h"
#include "ly_common.h"
#include "path.h"
#include "plugins_internal.h"
#include "schema_compile.h"
#include "set.h"
#include "tree.h"
#include "tree_data.h"
#include "tree_data_internal.h"
#include "tree_schema.h"
#include "tree_schema_internal.h"
#include "xml.h"
#include "xpath.h"
static const char *
ly_schema_get_prefix(const struct lys_module *mod, void *prefix_data)
{
const struct lysp_module *pmod = prefix_data;
LY_ARRAY_COUNT_TYPE u;
if (pmod->mod == mod) {
if (pmod->is_submod) {
return ((struct lysp_submodule *)pmod)->prefix;
} else {
return pmod->mod->prefix;
}
}
LY_ARRAY_FOR(pmod->imports, u) {
if (pmod->imports[u].module == mod) {
return pmod->imports[u].prefix;
}
}
return NULL;
}
static const char *
ly_schema_resolved_get_prefix(const struct lys_module *mod, void *prefix_data)
{
struct lysc_prefix *prefixes = prefix_data;
LY_ARRAY_COUNT_TYPE u;
LY_ARRAY_FOR(prefixes, u) {
if (prefixes[u].mod == mod) {
return prefixes[u].prefix;
}
}
return NULL;
}
static const char *
ly_xml_get_prefix(const struct lys_module *mod, void *prefix_data)
{
struct ly_set *mods = prefix_data;
uint32_t i;
assert(mods->count);
if (mods->objs[0] == mod) {
return NULL;
}
for (i = 1; i < mods->count; ++i) {
if (mods->objs[i] == mod) {
break;
}
}
if (i == mods->count) {
LY_CHECK_RET(ly_set_add(mods, (void *)mod, 1, NULL), NULL);
}
return mod->prefix;
}
static const char *
ly_json_get_prefix(const struct lys_module *mod, void *prefix_data)
{
const struct lys_module *local_mod = prefix_data;
return (local_mod == mod) ? NULL : mod->name;
}
const char *
ly_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data)
{
const char *prefix = NULL;
switch (format) {
case LY_VALUE_SCHEMA:
prefix = ly_schema_get_prefix(mod, prefix_data);
break;
case LY_VALUE_SCHEMA_RESOLVED:
prefix = ly_schema_resolved_get_prefix(mod, prefix_data);
break;
case LY_VALUE_XML:
case LY_VALUE_STR_NS:
prefix = ly_xml_get_prefix(mod, prefix_data);
break;
case LY_VALUE_CANON:
case LY_VALUE_JSON:
case LY_VALUE_LYB:
prefix = ly_json_get_prefix(mod, prefix_data);
break;
}
return prefix;
}
LIBYANG_API_DEF const char *
lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data)
{
return ly_get_prefix(mod, format, prefix_data);
}
LIBYANG_API_DEF LY_ERR
lyplg_type_compare_simple(const struct ly_ctx *ctx, const struct lyd_value *val1, const struct lyd_value *val2)
{
const char *can1, *can2;
can1 = lyd_value_get_canonical(ctx, val1);
can2 = lyd_value_get_canonical(ctx, val2);
if (can1 == can2) {
return LY_SUCCESS;
}
return LY_ENOT;
}
LIBYANG_API_DEF int
lyplg_type_sort_simple(const struct ly_ctx *ctx, const struct lyd_value *val1, const struct lyd_value *val2)
{
int cmp;
const char *can1, *can2;
can1 = lyd_value_get_canonical(ctx, val1);
can2 = lyd_value_get_canonical(ctx, val2);
if (can1 && can2) {
cmp = strcmp(can1, can2);
return cmp;
} else if (!can1 && can2) {
return -1;
} else if (can1 && !can2) {
return 1;
} else {
return 0;
}
}
LIBYANG_API_DEF const void *
lyplg_type_print_simple(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT UNUSED(format),
void *UNUSED(prefix_data), ly_bool *dynamic, uint64_t *value_size_bits)
{
if (dynamic) {
*dynamic = 0;
}
if (value_size_bits) {
*value_size_bits = ly_strlen(value->_canonical) * 8;
}
return value->_canonical;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_dup_simple(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
{
LY_ERR r;
if ((r = lydict_dup(ctx, original->_canonical, &dup->_canonical))) {
memset(dup, 0, sizeof *dup);
return r;
}
memcpy(dup->fixed_mem, original->fixed_mem, sizeof dup->fixed_mem);
dup->realtype = original->realtype;
return LY_SUCCESS;
}
LIBYANG_API_DEF void
lyplg_type_free_simple(const struct ly_ctx *ctx, struct lyd_value *value)
{
lydict_remove(ctx, value->_canonical);
value->_canonical = NULL;
}
LIBYANG_API_DEF void
lyplg_type_lyb_size_variable_bits(const struct lysc_type *UNUSED(type), enum lyplg_lyb_size_type *size_type,
uint64_t *UNUSED(fixed_size_bits))
{
*size_type = LYPLG_LYB_SIZE_VARIABLE_BITS;
}
LIBYANG_API_DEF void
lyplg_type_lyb_size_variable_bytes(const struct lysc_type *UNUSED(type), enum lyplg_lyb_size_type *size_type,
uint64_t *UNUSED(fixed_size_bits))
{
*size_type = LYPLG_LYB_SIZE_VARIABLE_BYTES;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, uint32_t value_len,
int64_t *ret, struct ly_err_item **err)
{
LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
*err = NULL;
for ( ; value_len && isspace(*value); ++value, --value_len) {}
if (!value || !value_len || !value[0]) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid type %s empty value.", datatype);
}
switch (ly_parse_int(value, value_len, min, max, base, ret)) {
case LY_EDENIED:
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
"Value \"%.*s\" is out of type %s min/max bounds.", (int)value_len, value, datatype);
case LY_SUCCESS:
return LY_SUCCESS;
default:
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
"Invalid type %s value \"%.*s\".", datatype, (int)value_len, value);
}
}
LIBYANG_API_DEF LY_ERR
lyplg_type_parse_uint(const char *datatype, int base, uint64_t max, const char *value, uint32_t value_len, uint64_t *ret,
struct ly_err_item **err)
{
LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
*err = NULL;
for ( ; value_len && isspace(*value); ++value, --value_len) {}
if (!value || !value_len || !value[0]) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid type %s empty value.", datatype);
}
*err = NULL;
switch (ly_parse_uint(value, value_len, max, base, ret)) {
case LY_EDENIED:
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
"Value \"%.*s\" is out of type %s min/max bounds.", (int)value_len, value, datatype);
case LY_SUCCESS:
return LY_SUCCESS;
default:
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
"Invalid type %s value \"%.*s\".", datatype, (int)value_len, value);
}
}
LIBYANG_API_DEF LY_ERR
lyplg_type_parse_dec64(uint8_t fraction_digits, const char *value, uint32_t value_len, int64_t *ret, struct ly_err_item **err)
{
LY_ERR ret_val;
char *valcopy = NULL;
uint32_t fraction = 0, size, len = 0, trailing_zeros;
int64_t d;
*err = NULL;
for ( ; value_len && isspace(*value); ++value, --value_len) {}
if (!value_len) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty decimal64 value.");
} else if (!isdigit(value[len]) && (value[len] != '-') && (value[len] != '+')) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid %" PRIu32 ". character of decimal64 value \"%.*s\".",
len + 1, (int)value_len, value);
}
if ((value[len] == '-') || (value[len] == '+')) {
++len;
}
while (len < value_len && isdigit(value[len])) {
++len;
}
trailing_zeros = 0;
if ((len < value_len) && ((value[len] != '.') || !isdigit(value[len + 1]))) {
goto decimal;
}
fraction = len;
++len;
while (len < value_len && isdigit(value[len])) {
if (value[len] == '0') {
++trailing_zeros;
} else {
trailing_zeros = 0;
}
++len;
}
len = len - trailing_zeros;
decimal:
if (fraction && (len - 1 - fraction > fraction_digits)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
"Value \"%.*s\" of decimal64 type exceeds defined number (%u) of fraction digits.",
(int)len, value, fraction_digits);
}
if (fraction) {
size = len + (fraction_digits - (len - 1 - fraction));
} else {
size = len + fraction_digits + 1;
}
if (len + trailing_zeros < value_len) {
uint64_t u;
for (u = len + trailing_zeros; u < value_len && isspace(value[u]); ++u) {}
if (u != value_len) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
"Invalid %" PRIu64 ". character of decimal64 value \"%.*s\".", u + 1, (int)value_len, value);
}
}
valcopy = malloc(size * sizeof *valcopy);
if (!valcopy) {
return ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
}
valcopy[size - 1] = '\0';
if (fraction) {
memcpy(&valcopy[0], &value[0], fraction);
memcpy(&valcopy[fraction], &value[fraction + 1], len - 1 - (fraction));
memset(&valcopy[len - 1], '0', fraction_digits - (len - 1 - fraction));
} else {
memcpy(&valcopy[0], &value[0], len);
memset(&valcopy[len], '0', fraction_digits);
}
ret_val = lyplg_type_parse_int("decimal64", LY_BASE_DEC, INT64_C(-9223372036854775807) - INT64_C(1),
INT64_C(9223372036854775807), valcopy, size - 1, &d, err);
if (!ret_val && ret) {
*ret = d;
}
free(valcopy);
return ret_val;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_validate_patterns(const struct ly_ctx *ctx, struct lysc_pattern **patterns, const char *str, uint32_t str_len,
struct ly_err_item **err)
{
LY_ERR r, rc = LY_SUCCESS;
struct ly_err_item *err_tmp = NULL;
LY_ARRAY_COUNT_TYPE u;
const void *pat_comp;
LY_CHECK_ARG_RET(NULL, str, err, LY_EINVAL);
*err = NULL;
LY_ARRAY_FOR(patterns, u) {
LY_CHECK_RET(ly_ctx_shared_data_pattern_get(ctx, patterns[u]->expr, patterns[u]->format, &pat_comp));
r = ly_pat_match(pat_comp, NULL, patterns[u]->format, str, str_len, &err_tmp);
if (r && (r != LY_ENOT)) {
*err = err_tmp;
return r;
}
if (((r == LY_ENOT) && !patterns[u]->inverted) || ((r == LY_SUCCESS) && patterns[u]->inverted)) {
char *eapptag = patterns[u]->eapptag ? strdup(patterns[u]->eapptag) : NULL;
if (patterns[u]->emsg) {
rc = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", patterns[u]->emsg);
} else {
rc = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
"Unsatisfied pattern - \"%.*s\" does not match %s\"%s\".", (int)str_len, str,
patterns[u]->inverted ? "inverted " : "", patterns[u]->expr);
}
ly_err_free(err_tmp);
return rc;
}
if (r == LY_ENOT) {
ly_err_free(err_tmp);
err_tmp = NULL;
}
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval,
uint32_t strval_len, struct ly_err_item **err)
{
LY_ARRAY_COUNT_TYPE u;
ly_bool is_length;
*err = NULL;
is_length = (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? 1 : 0;
LY_ARRAY_FOR(range->parts, u) {
if (basetype < LY_TYPE_DEC64) {
if ((uint64_t)value < range->parts[u].min_u64) {
char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
if (range->emsg) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
} else if ((uint64_t)value <= range->parts[u].max_u64) {
return LY_SUCCESS;
} else if (u == LY_ARRAY_COUNT(range->parts) - 1) {
char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
if (range->emsg) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
}
} else {
if (value < range->parts[u].min_64) {
char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
if (range->emsg) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
} else if (value <= range->parts[u].max_64) {
return LY_SUCCESS;
} else if (u == LY_ARRAY_COUNT(range->parts) - 1) {
char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
if (range->emsg) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
}
}
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_prefix_data_new(const struct ly_ctx *ctx, const void *value, uint32_t value_size, LY_VALUE_FORMAT format,
const void *prefix_data, LY_VALUE_FORMAT *format_p, void **prefix_data_p)
{
LY_CHECK_ARG_RET(ctx, value, format_p, prefix_data_p, LY_EINVAL);
*prefix_data_p = NULL;
return ly_store_prefix_data(ctx, value, value_size, format, prefix_data, format_p, (void **)prefix_data_p);
}
LIBYANG_API_DEF LY_ERR
lyplg_type_prefix_data_dup(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void *orig, void **dup)
{
LY_CHECK_ARG_RET(NULL, dup, LY_EINVAL);
*dup = NULL;
if (!orig) {
return LY_SUCCESS;
}
return ly_dup_prefix_data(ctx, format, orig, (void **)dup);
}
LIBYANG_API_DEF void
lyplg_type_prefix_data_free(LY_VALUE_FORMAT format, void *prefix_data)
{
ly_free_prefix_data(format, prefix_data);
}
static int
type_get_hints_base(uint32_t hints)
{
switch (hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) {
case LYD_VALHINT_DECNUM:
return LY_BASE_DEC;
case LYD_VALHINT_OCTNUM:
return LY_BASE_OCT;
case LYD_VALHINT_HEXNUM:
return LY_BASE_HEX;
default:
return 0;
}
}
LIBYANG_API_DEF LY_ERR
lyplg_type_check_value_size(const char *type_name, LY_VALUE_FORMAT format, uint64_t value_size_bits,
enum lyplg_lyb_size_type lyb_size_type, uint64_t lyb_fixed_size_bits, uint32_t *value_size,
struct ly_err_item **err)
{
if ((format == LY_VALUE_LYB) && (lyb_size_type == LYPLG_LYB_SIZE_FIXED_BITS) &&
(value_size_bits != lyb_fixed_size_bits)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid %s value size %" PRIu64 " b (expected %" PRIu64 " b).",
type_name, value_size_bits, lyb_fixed_size_bits);
} else if (((format != LY_VALUE_LYB) || (lyb_size_type == LYPLG_LYB_SIZE_VARIABLE_BYTES)) && (value_size_bits % 8)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid %s value size %" PRIu64 " b (expected full bytes).",
type_name, value_size_bits);
}
if (LYPLG_BITS2BYTES(value_size_bits) > UINT32_MAX) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid %s value size %" PRIu64 " b (too big).",
type_name, value_size_bits);
}
*value_size = LYPLG_BITS2BYTES(value_size_bits);
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_check_hints(uint32_t hints, const char *value, uint32_t value_size, LY_DATA_TYPE type, int *base,
struct ly_err_item **err)
{
LY_CHECK_ARG_RET(NULL, value || !value_size, err, LY_EINVAL);
*err = NULL;
if (!value) {
value = "";
}
switch (type) {
case LY_TYPE_UINT8:
case LY_TYPE_UINT16:
case LY_TYPE_UINT32:
case LY_TYPE_INT8:
case LY_TYPE_INT16:
case LY_TYPE_INT32:
LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) &&
!(hints & LYD_VALHINT_STRING_DATATYPES)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-number-encoded %s value \"%.*s\".",
lys_datatype2str(type), (int)value_size, value);
}
*base = type_get_hints_base(hints);
break;
case LY_TYPE_UINT64:
case LY_TYPE_INT64:
LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
if (!(hints & LYD_VALHINT_NUM64) &&
!(hints & LYD_VALHINT_STRING_DATATYPES)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-num64-encoded %s value \"%.*s\".",
lys_datatype2str(type), (int)value_size, value);
}
*base = type_get_hints_base(hints);
break;
case LY_TYPE_STRING:
case LY_TYPE_DEC64:
case LY_TYPE_ENUM:
case LY_TYPE_BITS:
case LY_TYPE_BINARY:
case LY_TYPE_IDENT:
case LY_TYPE_INST:
if (!(hints & LYD_VALHINT_STRING)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-string-encoded %s value \"%.*s\".",
lys_datatype2str(type), (int)value_size, value);
}
break;
case LY_TYPE_BOOL:
if (!(hints & LYD_VALHINT_BOOLEAN) &&
!(hints & LYD_VALHINT_STRING_DATATYPES)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-boolean-encoded %s value \"%.*s\".",
lys_datatype2str(type), (int)value_size, value);
}
break;
case LY_TYPE_EMPTY:
if (!(hints & LYD_VALHINT_EMPTY)) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-empty-encoded %s value \"%.*s\".",
lys_datatype2str(type), (int)value_size, value);
}
break;
case LY_TYPE_UNKNOWN:
case LY_TYPE_LEAFREF:
case LY_TYPE_UNION:
LOGINT_RET(NULL);
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_check_status(const struct lysc_node *ctx_node, uint16_t val_flags, LY_VALUE_FORMAT format, void *prefix_data,
const char *val_name, struct ly_err_item **err)
{
LY_ERR ret;
const struct lys_module *mod2;
uint16_t flg1, flg2;
if (format != LY_VALUE_SCHEMA) {
return LY_SUCCESS;
}
mod2 = ((struct lysp_module *)prefix_data)->mod;
if (mod2 == ctx_node->module) {
flg1 = (ctx_node->flags & LYS_STATUS_MASK) ? (ctx_node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
} else {
flg1 = LYS_STATUS_CURR;
}
flg2 = (val_flags & LYS_STATUS_MASK) ? (val_flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
if ((flg1 < flg2) && (ctx_node->module == mod2)) {
ret = ly_err_new(err, LY_EVALID, LYVE_REFERENCE, NULL, NULL,
"A %s definition \"%s\" is not allowed to reference %s value \"%s\".",
flg1 == LYS_STATUS_CURR ? "current" : "deprecated", ctx_node->name,
flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", val_name);
return ret;
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_lypath_check_status(const struct lysc_node *ctx_node, const struct ly_path *path, LY_VALUE_FORMAT format,
void *prefix_data, struct ly_err_item **err)
{
LY_ERR ret;
LY_ARRAY_COUNT_TYPE u;
const struct lys_module *val_mod;
const struct lysc_node *node;
uint16_t flg1, flg2;
if (format != LY_VALUE_SCHEMA) {
return LY_SUCCESS;
}
val_mod = ((struct lysp_module *)prefix_data)->mod;
if (val_mod == ctx_node->module) {
flg1 = (ctx_node->flags & LYS_STATUS_MASK) ? (ctx_node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
} else {
flg1 = LYS_STATUS_CURR;
}
LY_ARRAY_FOR(path, u) {
node = path[u].node;
flg2 = (node->flags & LYS_STATUS_MASK) ? (node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
if ((flg1 < flg2) && (val_mod == node->module)) {
ret = ly_err_new(err, LY_EVALID, LYVE_REFERENCE, NULL, NULL,
"A %s definition \"%s\" is not allowed to reference %s value \"%s\".",
flg1 == LYS_STATUS_CURR ? "current" : "deprecated", ctx_node->name,
flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", node->name);
return ret;
}
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, uint32_t value_len, uint32_t options,
LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *ctx_node, struct lys_glob_unres *unres,
struct ly_path **path, struct ly_err_item **err)
{
LY_ERR ret = LY_SUCCESS;
struct lyxp_expr *exp = NULL;
uint32_t prefix_opt = 0;
struct ly_err_item *e;
const char *err_fmt = NULL;
uint16_t oper;
LY_CHECK_ARG_RET(ctx, ctx, value, path, err, LY_EINVAL);
*path = NULL;
*err = NULL;
switch (format) {
case LY_VALUE_SCHEMA:
case LY_VALUE_SCHEMA_RESOLVED:
case LY_VALUE_XML:
prefix_opt = LY_PATH_PREFIX_MANDATORY;
break;
case LY_VALUE_CANON:
case LY_VALUE_LYB:
case LY_VALUE_JSON:
case LY_VALUE_STR_NS:
prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT;
break;
}
e = (struct ly_err_item *)ly_err_last(ctx);
ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
if (ret) {
err_fmt = "Invalid instance-identifier \"%.*s\" value - syntax error%s%s";
goto cleanup;
}
if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL);
if (ret) {
err_fmt = "Failed to implement a module referenced by instance-identifier \"%.*s\"%s%s";
goto cleanup;
}
}
oper = (ctx_node && (ctx_node->flags & LYS_IS_OUTPUT)) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
ret = ly_path_compile(ctx, ctx_node, exp, oper, LY_PATH_TARGET_SINGLE, 1, format, prefix_data, path);
if (ret) {
err_fmt = "Invalid instance-identifier \"%.*s\" value - semantic error%s%s";
goto cleanup;
}
cleanup:
lyxp_expr_free(exp);
if (ret) {
e = e ? e->next : (struct ly_err_item *)ly_err_last(ctx);
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, err_fmt, (int)value_len, value, e ? ": " : ".", e ? e->msg : "");
if (e) {
ly_err_clean((struct ly_ctx *)ctx, e);
}
ly_path_free(*path);
*path = NULL;
}
return ret;
}
LIBYANG_API_DEF void
lyplg_type_lypath_free(struct ly_path *path)
{
ly_path_free(path);
}
LIBYANG_API_DEF LY_ERR
lyplg_type_print_val(const struct lysc_node *node, const char *canon, LY_VALUE_FORMAT format, void *prefix_data,
const char **value)
{
LY_ERR r;
const struct lysc_type *type;
struct lyd_value storage = {0};
struct ly_err_item *err = NULL;
const char *v;
ly_bool dyn;
struct lyplg_type *type_plg;
type = ((struct lysc_node_leaf *)node)->type;
type_plg = LYSC_GET_TYPE_PLG(type->plugin_ref);
r = type_plg->store(node->module->ctx, type, canon, strlen(canon) * 8, LYPLG_TYPE_STORE_ONLY, LY_VALUE_CANON,
NULL, LYD_HINT_DATA, node, &storage, NULL, &err);
if (r && (r != LY_EINCOMPLETE)) {
if (err) {
ly_err_print(node->module->ctx, err, NULL, node);
ly_err_free(err);
}
return r;
}
v = type_plg->print(node->module->ctx, &storage, format, prefix_data, &dyn, NULL);
if (dyn) {
r = lydict_insert_zc(node->module->ctx, (char *)v, value);
} else {
r = lydict_dup(node->module->ctx, v, value);
}
type_plg->free(node->module->ctx, &storage);
return r;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_make_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres)
{
if (mod->implemented) {
return LY_SUCCESS;
}
LY_CHECK_RET(lys_implement(mod, features, unres));
LY_CHECK_RET(lys_compile(mod, &unres->ds_unres));
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_identity_isderived(const struct lysc_ident *base, const struct lysc_ident *der)
{
LY_ARRAY_COUNT_TYPE u;
assert(base->module->ctx == der->module->ctx);
LY_ARRAY_FOR(base->derived, u) {
if (der == base->derived[u]) {
return LY_SUCCESS;
}
if (!lyplg_type_identity_isderived(base->derived[u], der)) {
return LY_SUCCESS;
}
}
return LY_ENOTFOUND;
}
static LY_ERR
lyplg_type_resolve_leafref_get_target_path(const struct lyxp_expr *path, const struct lysc_node *ctx_node,
LY_VALUE_FORMAT format, void *prefix_data, const char *target_val, struct lyxp_expr **target_path)
{
LY_ERR rc = LY_SUCCESS;
uint8_t oper;
struct ly_path *p = NULL;
char *str_path = NULL, quot;
int len;
ly_bool list_key = 0;
*target_path = NULL;
oper = (ctx_node->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
if (ly_path_compile_leafref(ctx_node->module->ctx, ctx_node, path, oper, LY_PATH_TARGET_MANY, format,
prefix_data, &p)) {
return LY_ENOT;
}
if (lysc_is_key(p[LY_ARRAY_COUNT(p) - 1].node)) {
if ((LY_ARRAY_COUNT(p) >= 2) && (p[LY_ARRAY_COUNT(p) - 2].node->nodetype == LYS_LIST)) {
if ((path->tokens[path->used - 1] == LYXP_TOKEN_NAMETEST) &&
(path->tokens[path->used - 2] == LYXP_TOKEN_OPER_PATH) &&
(path->tokens[path->used - 3] == LYXP_TOKEN_NAMETEST)) {
list_key = 1;
}
}
}
if (list_key) {
len = path->tok_pos[path->used - 3] + path->tok_len[path->used - 3];
quot = strchr(target_val, '\'') ? '\"' : '\'';
if (asprintf(&str_path, "%.*s[%s=%c%s%c]/%s", len, path->expr, path->expr + path->tok_pos[path->used - 1],
quot, target_val, quot, path->expr + path->tok_pos[path->used - 1]) == -1) {
LOGMEM(ctx_node->module->ctx);
rc = LY_EMEM;
goto cleanup;
}
} else {
assert(p[LY_ARRAY_COUNT(p) - 1].node->nodetype & LYD_NODE_TERM);
quot = strchr(target_val, '\'') ? '\"' : '\'';
if (asprintf(&str_path, "%s[.=%c%s%c]", path->expr, quot, target_val, quot) == -1) {
LOGMEM(ctx_node->module->ctx);
rc = LY_EMEM;
goto cleanup;
}
}
LY_CHECK_GOTO(lyxp_expr_parse(ctx_node->module->ctx, NULL, str_path, 0, 1, target_path), cleanup);
cleanup:
ly_path_free(p);
free(str_path);
return rc;
}
LIBYANG_API_DEF LY_ERR
lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct lyd_node *node, struct lyd_value *value,
const struct lyd_node *tree, struct ly_set **targets, char **errmsg)
{
LY_ERR rc = LY_SUCCESS;
struct lyxp_expr *target_path = NULL;
const struct ly_err_item *e;
struct lyxp_set set = {0};
const char *val_str, *xp_err_msg;
uint32_t i;
int r;
struct lyplg_type *type_plg;
LY_CHECK_ARG_RET(NULL, lref, node, value, errmsg, LY_EINVAL);
*errmsg = NULL;
if (targets) {
*targets = NULL;
}
val_str = lyd_value_get_canonical(LYD_CTX(node), value);
if (!strchr(val_str, '\"') || !strchr(val_str, '\'')) {
r = lyplg_type_resolve_leafref_get_target_path(lref->path, node->schema, LY_VALUE_SCHEMA_RESOLVED,
lref->prefixes, val_str, &target_path);
if (r == LY_ENOT) {
goto cleanup;
} else if (r) {
rc = r;
goto cleanup;
}
}
rc = lyxp_eval(LYD_CTX(node), target_path ? target_path : lref->path, node->schema->module,
LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, node, node, tree, NULL, &set, LYXP_IGNORE_WHEN);
if (rc) {
e = ly_err_last(LYD_CTX(node));
if (e && (e->err == rc)) {
xp_err_msg = e->msg;
} else {
xp_err_msg = NULL;
}
if (xp_err_msg) {
r = asprintf(errmsg, "Invalid leafref value \"%s\" - XPath evaluation error (%s).", val_str, xp_err_msg);
} else {
r = asprintf(errmsg, "Invalid leafref value \"%s\" - XPath evaluation error.", val_str);
}
if (r == -1) {
*errmsg = NULL;
rc = LY_EMEM;
}
goto cleanup;
}
type_plg = LYSC_GET_TYPE_PLG(lref->plugin_ref);
if (target_path) {
i = 0;
} else {
for (i = 0; i < set.used; ++i) {
if (set.val.nodes[i].type != LYXP_NODE_ELEM) {
continue;
}
if (((struct lyd_node_term *)set.val.nodes[i].node)->value.realtype != value->realtype) {
continue;
}
if (!type_plg->compare(LYD_CTX(node), &((struct lyd_node_term *)set.val.nodes[i].node)->value, value)) {
break;
}
}
}
if (i == set.used) {
rc = LY_ENOTFOUND;
if (asprintf(errmsg, LY_ERRMSG_NOLREF_VAL, val_str, lref->path->expr) == -1) {
*errmsg = NULL;
rc = LY_EMEM;
}
goto cleanup;
}
if (targets) {
LY_CHECK_GOTO(rc = ly_set_new(targets), cleanup);
for (i = 0; i < set.used; ++i) {
if (set.val.nodes[i].type != LYXP_NODE_ELEM) {
continue;
}
if (((struct lyd_node_term *)set.val.nodes[i].node)->value.realtype != value->realtype) {
continue;
}
if (!type_plg->compare(LYD_CTX(node), &((struct lyd_node_term *)set.val.nodes[i].node)->value, value)) {
rc = ly_set_add(*targets, set.val.nodes[i].node, 0, NULL);
LY_CHECK_GOTO(rc, cleanup);
}
}
}
cleanup:
lyxp_expr_free(target_path);
lyxp_set_free_content(&set);
return rc;
}
LIBYANG_API_DEF uint64_t
lyplg_type_get_highest_set_bit_pos(uint64_t num)
{
uint64_t pos = 0;
while (num) {
num >>= 1;
++pos;
}
return pos;
}