/**
* @file common.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief common libyang routines implementations
*
* Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include "common.h"
#include "parser.h"
#include "xpath.h"
#include "context.h"
THREAD_LOCAL enum int_log_opts log_opt;
THREAD_LOCAL int8_t ly_errno_glob;
API LY_ERR *
ly_errno_glob_address(void)
{
return (LY_ERR *)&ly_errno_glob;
}
API LY_VECODE
ly_vecode(const struct ly_ctx *ctx)
{
struct ly_err_item *i;
i = ly_err_first(ctx);
if (i) {
return i->prev->vecode;
}
return 0;
}
API const char *
ly_errmsg(const struct ly_ctx *ctx)
{
struct ly_err_item *i;
i = ly_err_first(ctx);
if (i) {
return i->prev->msg;
}
return NULL;
}
API const char *
ly_errpath(const struct ly_ctx *ctx)
{
struct ly_err_item *i;
i = ly_err_first(ctx);
if (i) {
return i->prev->path;
}
return NULL;
}
API const char *
ly_errapptag(const struct ly_ctx *ctx)
{
struct ly_err_item *i;
i = ly_err_first(ctx);
if (i) {
return i->prev->apptag;
}
return NULL;
}
API struct ly_err_item *
ly_err_first(const struct ly_ctx *ctx)
{
if (!ctx) {
return NULL;
}
return pthread_getspecific(ctx->errlist_key);
}
void
ly_err_free(void *ptr)
{
struct ly_err_item *i, *next;
/* clean the error list */
for (i = (struct ly_err_item *)ptr; i; i = next) {
next = i->next;
free(i->msg);
free(i->path);
free(i->apptag);
free(i);
}
}
API void
ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
{
struct ly_err_item *i, *first;
first = ly_err_first(ctx);
if (first == eitem) {
eitem = NULL;
}
if (eitem) {
/* disconnect the error */
for (i = first; i && (i->next != eitem); i = i->next);
assert(i);
i->next = NULL;
first->prev = i;
/* free this err and newer */
ly_err_free(eitem);
/* update errno */
ly_errno = i->no;
} else {
/* free all err */
ly_err_free(first);
pthread_setspecific(ctx->errlist_key, NULL);
/* also clean errno */
ly_errno = LY_SUCCESS;
}
}
#ifndef __USE_GNU
char *
get_current_dir_name(void)
{
char tmp[PATH_MAX];
char *retval;
if (getcwd(tmp, sizeof(tmp))) {
retval = strdup(tmp);
LY_CHECK_ERR_RETURN(!retval, LOGMEM(NULL), NULL);
return retval;
}
return NULL;
}
#endif
const char *
strpbrk_backwards(const char *s, const char *accept, unsigned int s_len)
{
const char *sc;
for (; *s != '\0' && s_len; --s, --s_len) {
for (sc = accept; *sc != '\0'; ++sc) {
if (*s == *sc) {
return s;
}
}
}
return s;
}
char *
strnchr(const char *s, int c, unsigned int len)
{
for (; *s != (char)c; ++s, --len) {
if ((*s == '\0') || (!len)) {
return NULL;
}
}
return (char *)s;
}
const char *
strnodetype(LYS_NODE type)
{
switch (type) {
case LYS_UNKNOWN:
return NULL;
case LYS_AUGMENT:
return "augment";
case LYS_CONTAINER:
return "container";
case LYS_CHOICE:
return "choice";
case LYS_LEAF:
return "leaf";
case LYS_LEAFLIST:
return "leaf-list";
case LYS_LIST:
return "list";
case LYS_ANYXML:
return "anyxml";
case LYS_GROUPING:
return "grouping";
case LYS_CASE:
return "case";
case LYS_INPUT:
return "input";
case LYS_OUTPUT:
return "output";
case LYS_NOTIF:
return "notification";
case LYS_RPC:
return "rpc";
case LYS_USES:
return "uses";
case LYS_ACTION:
return "action";
case LYS_ANYDATA:
return "anydata";
case LYS_EXT:
return "extension instance";
}
return NULL;
}
const char *
transform_module_name2import_prefix(const struct lys_module *module, const char *module_name)
{
uint16_t i;
if (!module_name) {
return NULL;
}
if (!strcmp(lys_main_module(module)->name, module_name)) {
/* the same for module and submodule */
return module->prefix;
}
for (i = 0; i < module->imp_size; ++i) {
if (!strcmp(module->imp[i].module->name, module_name)) {
return module->imp[i].prefix;
}
}
return NULL;
}
static int
_transform_json2xml_subexp(const struct lys_module *module, const char *expr, char **out, size_t *out_used, size_t *out_size, int schema, int inst_id, const char ***prefixes,
const char ***namespaces, uint32_t *ns_count)
{
const char *cur_expr, *end, *prefix, *literal;
char *name;
size_t name_len;
const struct lys_module *mod = NULL, *prev_mod = NULL;
uint32_t i, j;
struct lyxp_expr *exp;
struct ly_ctx *ctx = module->ctx;
enum int_log_opts prev_ilo;
assert(module && expr && ((!prefixes && !namespaces && !ns_count) || (prefixes && namespaces && ns_count)));
exp = lyxp_parse_expr(ctx, expr);
LY_CHECK_RETURN(!exp, 1);
for (i = 0; i < exp->used; ++i) {
cur_expr = &exp->expr[exp->expr_pos[i]];
/* copy WS */
if (i && ((end = exp->expr + exp->expr_pos[i - 1] + exp->tok_len[i - 1]) != cur_expr)) {
strncpy(&(*out)[*out_used], end, cur_expr - end);
(*out_used) += cur_expr - end;
}
if ((exp->tokens[i] == LYXP_TOKEN_NAMETEST) && ((end = strnchr(cur_expr, ':', exp->tok_len[i])) || inst_id)) {
/* get the module */
if (!schema) {
if (end) {
name_len = end - cur_expr;
name = strndup(cur_expr, name_len);
mod = ly_ctx_get_module(module->ctx, name, NULL, 0);
if (module->ctx->data_clb) {
if (!mod) {
mod = module->ctx->data_clb(module->ctx, name, NULL, 0, module->ctx->data_clb_data);
} else if (!mod->implemented) {
mod = module->ctx->data_clb(module->ctx, name, mod->ns, LY_MODCLB_NOT_IMPLEMENTED, module->ctx->data_clb_data);
}
}
free(name);
if (!mod) {
LOGVAL(ctx, LYE_INMOD_LEN, LY_VLOG_NONE, NULL, name_len, cur_expr);
goto error;
}
prev_mod = mod;
} else {
mod = prev_mod;
if (!mod) {
LOGINT(ctx);
goto error;
}
name_len = 0;
end = cur_expr;
}
prefix = mod->prefix;
} else {
if (end) {
name_len = end - cur_expr;
} else {
name_len = strlen(cur_expr);
end = cur_expr;
}
name = strndup(cur_expr, name_len);
prefix = transform_module_name2import_prefix(module, name);
free(name);
if (!prefix) {
LOGVAL(ctx, LYE_INMOD_LEN, LY_VLOG_NONE, NULL, name_len, cur_expr);
goto error;
}
}
/* remember the namespace definition (only if it's new) */
if (!schema && ns_count) {
for (j = 0; j < *ns_count; ++j) {
if (ly_strequal((*namespaces)[j], mod->ns, 1)) {
break;
}
}
if (j == *ns_count) {
++(*ns_count);
*prefixes = ly_realloc(*prefixes, *ns_count * sizeof **prefixes);
LY_CHECK_ERR_GOTO(!(*prefixes), LOGMEM(ctx), error);
*namespaces = ly_realloc(*namespaces, *ns_count * sizeof **namespaces);
LY_CHECK_ERR_GOTO(!(*namespaces), LOGMEM(ctx), error);
(*prefixes)[*ns_count - 1] = mod->prefix;
(*namespaces)[*ns_count - 1] = mod->ns;
}
}
/* adjust out size (it can even decrease in some strange cases) */
*out_size += strlen(prefix) + 1 - name_len;
*out = ly_realloc(*out, *out_size);
LY_CHECK_ERR_GOTO(!(*out), LOGMEM(ctx), error);
/* copy the model name */
strcpy(&(*out)[*out_used], prefix);
*out_used += strlen(prefix);
if (!name_len) {
/* we are adding the prefix, so also ':' */
(*out)[*out_used] = ':';
++(*out_used);
}
/* copy the rest */
strncpy(&(*out)[*out_used], end, exp->tok_len[i] - name_len);
*out_used += exp->tok_len[i] - name_len;
} else if ((exp->tokens[i] == LYXP_TOKEN_LITERAL) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) {
/* copy begin quote */
(*out)[*out_used] = cur_expr[0];
++(*out_used);
/* skip quotes */
literal = lydict_insert(module->ctx, cur_expr + 1, exp->tok_len[i] - 2);
/* parse literals as subexpressions if possible, otherwise treat as a literal */
ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
if (_transform_json2xml_subexp(module, literal, out, out_used, out_size, schema, inst_id, prefixes, namespaces, ns_count)) {
strncpy(&(*out)[*out_used], literal, exp->tok_len[i] - 2);
*out_used += exp->tok_len[i] - 2;
}
ly_ilo_restore(NULL, prev_ilo, NULL, 0);
lydict_remove(module->ctx, literal);
/* copy end quote */
(*out)[*out_used] = cur_expr[exp->tok_len[i] - 1];
++(*out_used);
} else {
strncpy(&(*out)[*out_used], &exp->expr[exp->expr_pos[i]], exp->tok_len[i]);
*out_used += exp->tok_len[i];
}
}
lyxp_expr_free(exp);
return 0;
error:
if (!schema && ns_count) {
free(*prefixes);
free(*namespaces);
}
lyxp_expr_free(exp);
return 1;
}
static const char *
_transform_json2xml(const struct lys_module *module, const char *expr, int schema, int inst_id, const char ***prefixes,
const char ***namespaces, uint32_t *ns_count)
{
char *out;
size_t out_size, out_used;
int ret;
assert(module && expr && ((!prefixes && !namespaces && !ns_count) || (prefixes && namespaces && ns_count)));
if (ns_count) {
*ns_count = 0;
*prefixes = NULL;
*namespaces = NULL;
}
if (!expr[0]) {
/* empty value */
return lydict_insert(module->ctx, expr, 0);
}
out_size = strlen(expr) + 1;
out = malloc(out_size);
LY_CHECK_ERR_RETURN(!out, LOGMEM(module->ctx), NULL);
out_used = 0;
ret = _transform_json2xml_subexp(module, expr, &out, &out_used, &out_size, schema, inst_id, prefixes, namespaces, ns_count);
if (!ret) {
out[out_used] = '\0';
return lydict_insert_zc(module->ctx, out);
}
free(out);
return NULL;
}
const char *
transform_json2xml(const struct lys_module *module, const char *expr, int inst_id, const char ***prefixes,
const char ***namespaces, uint32_t *ns_count)
{
return _transform_json2xml(module, expr, 0, inst_id, prefixes, namespaces, ns_count);
}
const char *
transform_json2schema(const struct lys_module *module, const char *expr)
{
return _transform_json2xml(module, expr, 1, 0, NULL, NULL, NULL);
}
static int
transform_xml2json_subexp(struct ly_ctx *ctx, const char *expr, char **out, size_t *out_used, size_t *out_size,
struct lyxml_elem *xml, int inst_id, int use_ctx_data_clb)
{
const char *end, *cur_expr, *literal;
char *prefix;
uint16_t i;
enum int_log_opts prev_ilo;
size_t pref_len;
const struct lys_module *mod, *prev_mod = NULL;
const struct lyxml_ns *ns;
struct lyxp_expr *exp;
exp = lyxp_parse_expr(ctx, expr);
if (!exp) {
return 1;
}
for (i = 0; i < exp->used; ++i) {
cur_expr = &exp->expr[exp->expr_pos[i]];
/* copy WS */
if (i && ((end = exp->expr + exp->expr_pos[i - 1] + exp->tok_len[i - 1]) != cur_expr)) {
strncpy(&(*out)[*out_used], end, cur_expr - end);
(*out_used) += cur_expr - end;
}
if ((exp->tokens[i] == LYXP_TOKEN_NAMETEST) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) {
/* get the module */
pref_len = end - cur_expr;
prefix = strndup(cur_expr, pref_len);
if (!prefix) {
LOGMEM(ctx);
goto error;
}
ns = lyxml_get_ns(xml, prefix);
free(prefix);
if (!ns) {
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, xml, "namespace prefix");
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "XML namespace with prefix \"%.*s\" not defined.", pref_len, cur_expr);
goto error;
}
mod = ly_ctx_get_module_by_ns(ctx, ns->value, NULL, 0);
if (use_ctx_data_clb && ctx->data_clb) {
if (!mod) {
mod = ctx->data_clb(ctx, NULL, ns->value, 0, ctx->data_clb_data);
} else if (!mod->implemented) {
mod = ctx->data_clb(ctx, mod->name, mod->ns, LY_MODCLB_NOT_IMPLEMENTED, ctx->data_clb_data);
}
}
if (!mod) {
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, xml, "module namespace");
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Module with the namespace \"%s\" could not be found.", ns->value);
goto error;
}
if (!inst_id || (mod != prev_mod)) {
/* adjust out size (it can even decrease in some strange cases) */
*out_size += strlen(mod->name) - pref_len;
*out = ly_realloc(*out, *out_size);
if (!(*out)) {
LOGMEM(ctx);
goto error;
}
/* copy the model name */
strcpy(&(*out)[*out_used], mod->name);
*out_used += strlen(mod->name);
} else {
/* skip ':' */
++end;
++pref_len;
}
/* remember previous model name */
prev_mod = mod;
/* copy the rest */
strncpy(&(*out)[*out_used], end, exp->tok_len[i] - pref_len);
*out_used += exp->tok_len[i] - pref_len;
} else if ((exp->tokens[i] == LYXP_TOKEN_NAMETEST) && inst_id) {
LOGVAL(ctx, LYE_XML_INVAL, LY_VLOG_XML, xml, "namespace prefix");
LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Node name is missing module prefix.");
goto error;
} else if ((exp->tokens[i] == LYXP_TOKEN_LITERAL) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) {
/* copy begin quote */
(*out)[*out_used] = cur_expr[0];
++(*out_used);
/* skip quotes */
literal = lydict_insert(ctx, cur_expr + 1, exp->tok_len[i] - 2);
/* parse literals as subexpressions if possible, otherwise treat as a literal, do not log */
prev_ilo = log_opt;
log_opt = ILO_IGNORE;
if (transform_xml2json_subexp(ctx, literal, out, out_used, out_size, xml, inst_id, use_ctx_data_clb)) {
strncpy(&(*out)[*out_used], literal, exp->tok_len[i] - 2);
*out_used += exp->tok_len[i] - 2;
}
log_opt = prev_ilo;
lydict_remove(ctx, literal);
/* copy end quote */
(*out)[*out_used] = cur_expr[exp->tok_len[i] - 1];
++(*out_used);
} else {
strncpy(&(*out)[*out_used], &exp->expr[exp->expr_pos[i]], exp->tok_len[i]);
*out_used += exp->tok_len[i];
}
}
lyxp_expr_free(exp);
return 0;
error:
lyxp_expr_free(exp);
return 1;
}
const char *
transform_xml2json(struct ly_ctx *ctx, const char *expr, struct lyxml_elem *xml, int inst_id, int use_ctx_data_clb)
{
char *out;
size_t out_size, out_used;
int ret;
out_size = strlen(expr) + 1;
out = malloc(out_size);
if (!out) {
LOGMEM(ctx);
return NULL;
}
out_used = 0;
ret = transform_xml2json_subexp(ctx, expr, &out, &out_used, &out_size, xml, inst_id, use_ctx_data_clb);
if (!ret) {
out[out_used] = '\0';
return lydict_insert_zc(ctx, out);
}
free(out);
return NULL;
}
API char *
ly_path_xml2json(struct ly_ctx *ctx, const char *xml_path, struct lyxml_elem *xml)
{
const char *json_path;
char *ret = NULL;
if (!ctx || !xml_path || !xml) {
LOGARG;
return NULL;
}
json_path = transform_xml2json(ctx, xml_path, xml, 0, 1);
if (json_path) {
ret = strdup(json_path);
lydict_remove(ctx, json_path);
}
return ret;
}
const char *
transform_schema2json(const struct lys_module *module, const char *expr)
{
const char *end, *cur_expr, *ptr;
char *out;
uint16_t i;
size_t out_size, out_used, pref_len;
const struct lys_module *mod;
struct ly_ctx *ctx = module->ctx;
struct lyxp_expr *exp = NULL;
out_size = strlen(expr) + 1;
out = malloc(out_size);
LY_CHECK_ERR_RETURN(!out, LOGMEM(ctx), NULL);
out_used = 0;
exp = lyxp_parse_expr(ctx, expr);
LY_CHECK_ERR_GOTO(!exp, , error);
for (i = 0; i < exp->used; ++i) {
cur_expr = &exp->expr[exp->expr_pos[i]];
/* copy WS */
if (i && ((end = exp->expr + exp->expr_pos[i - 1] + exp->tok_len[i - 1]) != cur_expr)) {
strncpy(&out[out_used], end, cur_expr - end);
out_used += cur_expr - end;
}
if ((exp->tokens[i] == LYXP_TOKEN_NAMETEST) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) {
/* get the module */
pref_len = end - cur_expr;
mod = lyp_get_module(module, cur_expr, pref_len, NULL, 0, 0);
if (!mod) {
LOGVAL(ctx, LYE_INMOD_LEN, LY_VLOG_NONE, NULL, pref_len, cur_expr);
goto error;
}
/* adjust out size (it can even decrease in some strange cases) */
out_size += strlen(mod->name) - pref_len;
out = ly_realloc(out, out_size);
LY_CHECK_ERR_GOTO(!out, LOGMEM(ctx), error);
/* copy the model name */
strcpy(&out[out_used], mod->name);
out_used += strlen(mod->name);
/* copy the rest */
strncpy(&out[out_used], end, exp->tok_len[i] - pref_len);
out_used += exp->tok_len[i] - pref_len;
} else if ((exp->tokens[i] == LYXP_TOKEN_LITERAL) && (end = strnchr(cur_expr, ':', exp->tok_len[i]))) {
ptr = end;
while (isalnum(ptr[-1]) || (ptr[-1] == '_') || (ptr[-1] == '-') || (ptr[-1] == '.')) {
--ptr;
}
/* get the module */
pref_len = end - ptr;
mod = lyp_get_module(module, ptr, pref_len, NULL, 0, 0);
if (mod) {
/* adjust out size (it can even decrease in some strange cases) */
out_size += strlen(mod->name) - pref_len;
out = ly_realloc(out, out_size);
LY_CHECK_ERR_GOTO(!out, LOGMEM(ctx), error);
/* copy any beginning */
strncpy(&out[out_used], cur_expr, ptr - cur_expr);
out_used += ptr - cur_expr;
/* copy the model name */
strcpy(&out[out_used], mod->name);
out_used += strlen(mod->name);
/* copy the rest */
strncpy(&out[out_used], end, (exp->tok_len[i] - pref_len) - (ptr - cur_expr));
out_used += (exp->tok_len[i] - pref_len) - (ptr - cur_expr);
} else {
strncpy(&out[out_used], &exp->expr[exp->expr_pos[i]], exp->tok_len[i]);
out_used += exp->tok_len[i];
}
} else {
strncpy(&out[out_used], &exp->expr[exp->expr_pos[i]], exp->tok_len[i]);
out_used += exp->tok_len[i];
}
}
out[out_used] = '\0';
lyxp_expr_free(exp);
return lydict_insert_zc(module->ctx, out);
error:
free(out);
lyxp_expr_free(exp);
return NULL;
}
const char *
transform_iffeat_schema2json(const struct lys_module *module, const char *expr)
{
const char *in, *id;
char *out, *col;
size_t out_size, out_used, id_len, rc;
const struct lys_module *mod;
struct ly_ctx *ctx = module->ctx;
in = expr;
out_size = strlen(in) + 1;
out = malloc(out_size);
LY_CHECK_ERR_RETURN(!out, LOGMEM(ctx), NULL);
out_used = 0;
while (1) {
col = strchr(in, ':');
/* we're finished, copy the remaining part */
if (!col) {
strcpy(&out[out_used], in);
out_used += strlen(in) + 1;
assert(out_size == out_used);
return lydict_insert_zc(ctx, out);
}
id = strpbrk_backwards(col - 1, " \f\n\r\t\v(", (col - in) - 1);
if ((id[0] == ' ') || (id[0] == '\f') || (id[0] == '\n') || (id[0] == '\r') ||
(id[0] == '\t') || (id[0] == '\v') || (id[0] == '(')) {
++id;
}
id_len = col - id;
rc = parse_identifier(id);
if (rc < id_len) {
LOGVAL(ctx, LYE_INCHAR, LY_VLOG_NONE, NULL, id[rc], &id[rc]);
free(out);
return NULL;
}
/* get the module */
mod = lyp_get_module(module, id, id_len, NULL, 0, 0);
if (!mod) {
LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Module prefix \"%.*s\" is unknown.", id_len, id);
free(out);
return NULL;
}
/* adjust out size (it can even decrease in some strange cases) */
out_size += strlen(mod->name) - id_len;
out = ly_realloc(out, out_size);
LY_CHECK_ERR_RETURN(!out, LOGMEM(ctx), NULL);
/* copy the data before prefix */
strncpy(&out[out_used], in, id - in);
out_used += id - in;
/* copy the model name */
strcpy(&out[out_used], mod->name);
out_used += strlen(mod->name);
/* copy ':' */
out[out_used] = ':';
++out_used;
/* finally adjust in pointer for next round */
in = col + 1;
}
/* unreachable */
LOGINT(ctx);
return NULL;
}
static int
transform_json2xpath_subexpr(const struct lys_module *cur_module, const struct lys_module *prev_mod, struct lyxp_expr *exp,
uint32_t *i, enum lyxp_token end_token, char **out, size_t *out_used, size_t *out_size)
{
const char *cur_expr, *end, *ptr;
size_t name_len;
char *name;
const struct lys_module *mod;
struct ly_ctx *ctx = cur_module->ctx;
while (*i < exp->used) {
if (exp->tokens[*i] == end_token) {
return 0;
}
cur_expr = &exp->expr[exp->expr_pos[*i]];
/* copy WS */
if (*i && ((end = exp->expr + exp->expr_pos[*i - 1] + exp->tok_len[*i - 1]) != cur_expr)) {
strncpy(*out + *out_used, end, cur_expr - end);
*out_used += cur_expr - end;
}
if (exp->tokens[*i] == LYXP_TOKEN_BRACK1) {
/* copy "[" */
strncpy(*out + *out_used, &exp->expr[exp->expr_pos[*i]], exp->tok_len[*i]);
*out_used += exp->tok_len[*i];
++(*i);
/* call recursively because we need to remember current prev_mod for after the predicate */
if (transform_json2xpath_subexpr(cur_module, prev_mod, exp, i, LYXP_TOKEN_BRACK2, out, out_used, out_size)) {
return -1;
}
if (*i >= exp->used) {
LOGVAL(ctx, LYE_XPATH_EOF, LY_VLOG_NONE, NULL);
return -1;
}
/* copy "]" */
strncpy(*out + *out_used, &exp->expr[exp->expr_pos[*i]], exp->tok_len[*i]);
*out_used += exp->tok_len[*i];
} else if (exp->tokens[*i] == LYXP_TOKEN_NAMETEST) {
if ((end = strnchr(cur_expr, ':', exp->tok_len[*i]))) {
/* there is a prefix, get the module */
name_len = end - cur_expr;
name = strndup(cur_expr, name_len);
prev_mod = ly_ctx_get_module(ctx, name, NULL, 1);
free(name);
if (!prev_mod) {
LOGVAL(ctx, LYE_INMOD_LEN, LY_VLOG_NONE, NULL, name_len ? name_len : exp->tok_len[*i], cur_expr);
return -1;
}
/* skip ":" */
++end;
++name_len;
} else {
end = cur_expr;
name_len = 0;
}
/* do we print the module name? (always for "*" if there was any, it's an exception) */
if (((prev_mod != cur_module) && (end[0] != '*')) || (name_len && (end[0] == '*'))) {
/* adjust out size (it can even decrease in some strange cases) */
*out_size += (strlen(prev_mod->name) - name_len) + 1;
*out = ly_realloc(*out, *out_size);
LY_CHECK_ERR_RETURN(!*out, LOGMEM(ctx), -1);
/* copy the model name */
strcpy(*out + *out_used, prev_mod->name);
*out_used += strlen(prev_mod->name);
/* print ":" */
(*out)[*out_used] = ':';
++(*out_used);
}
/* copy the rest */
strncpy(*out + *out_used, end, exp->tok_len[*i] - name_len);
*out_used += exp->tok_len[*i] - name_len;
} else if ((exp->tokens[*i] == LYXP_TOKEN_LITERAL) && (end = strnchr(cur_expr, ':', exp->tok_len[*i]))) {
ptr = end;
while (isalnum(ptr[-1]) || (ptr[-1] == '_') || (ptr[-1] == '-') || (ptr[-1] == '.')) {
--ptr;
}
/* get the module, but it may actually not be a module name */
name_len = end - ptr;
name = strndup(ptr, name_len);
mod = ly_ctx_get_module(ctx, name, NULL, 1);
free(name);
if (mod && (mod != cur_module)) {
/* adjust out size (it can even decrease in some strange cases) */
*out_size += strlen(mod->name) - name_len;
*out = ly_realloc(*out, *out_size);
LY_CHECK_ERR_RETURN(!*out, LOGMEM(ctx), -1);
/* copy any beginning */
strncpy(*out + *out_used, cur_expr, ptr - cur_expr);
*out_used += ptr - cur_expr;
/* copy the model name */
strcpy(*out + *out_used, mod->name);
*out_used += strlen(mod->name);
/* copy the rest */
strncpy(*out + *out_used, end, (exp->tok_len[*i] - name_len) - (ptr - cur_expr));
*out_used += (exp->tok_len[*i] - name_len) - (ptr - cur_expr);
} else {
strncpy(*out + *out_used, &exp->expr[exp->expr_pos[*i]], exp->tok_len[*i]);
*out_used += exp->tok_len[*i];
}
} else {
strncpy(*out + *out_used, &exp->expr[exp->expr_pos[*i]], exp->tok_len[*i]);
*out_used += exp->tok_len[*i];
}
++(*i);
}
return 0;
}
char *
transform_json2xpath(const struct lys_module *cur_module, const char *expr)
{
char *out;
size_t out_size, out_used;
uint32_t i;
struct lyxp_expr *exp;
assert(cur_module && expr);
out_size = strlen(expr) + 1;
out = malloc(out_size);
LY_CHECK_ERR_RETURN(!out, LOGMEM(cur_module->ctx), NULL);
out_used = 0;
exp = lyxp_parse_expr(cur_module->ctx, expr);
LY_CHECK_ERR_RETURN(!exp, free(out), NULL);
i = 0;
if (transform_json2xpath_subexpr(cur_module, cur_module, exp, &i, LYXP_TOKEN_NONE, &out, &out_used, &out_size)) {
goto error;
}
out[out_used] = '\0';
lyxp_expr_free(exp);
return out;
error:
free(out);
lyxp_expr_free(exp);
return NULL;
}
static int
ly_path_data2schema_copy_token(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t cur_exp, char **out, uint16_t *out_used)
{
uint16_t len;
for (len = exp->tok_len[cur_exp]; isspace(exp->expr[exp->expr_pos[cur_exp] + len]); ++len);
*out = ly_realloc(*out, *out_used + len);
LY_CHECK_ERR_RETURN(!(*out), LOGMEM(ctx), -1);
sprintf(*out + *out_used - 1, "%.*s", len, exp->expr + exp->expr_pos[cur_exp]);
*out_used += len;
return 0;
}
static int
ly_path_data2schema_subexp(const struct ly_ctx *ctx, const struct lys_node *orig_parent, const struct lys_module *cur_mod,
struct lyxp_expr *exp, uint16_t *cur_exp, char **out, uint16_t *out_used)
{
uint16_t j, k, len, slash;
char *str = NULL, *col;
const struct lys_node *node, *node2, *parent;
enum lyxp_token end_token = 0;
int first, path_lost;
switch (exp->tokens[*cur_exp]) {
case LYXP_TOKEN_BRACK1:
end_token = LYXP_TOKEN_BRACK2;
if (ly_path_data2schema_copy_token(ctx, exp, *cur_exp, out, out_used)) {
goto error;
}
++(*cur_exp);
first = 0;
break;
case LYXP_TOKEN_PAR1:
end_token = LYXP_TOKEN_PAR2;
if (ly_path_data2schema_copy_token(ctx, exp, *cur_exp, out, out_used)) {
goto error;
}
++(*cur_exp);
first = 0;
break;
case LYXP_TOKEN_OPERATOR_PATH:
first = (orig_parent) ? 0 : 1;
break;
default:
first = 1;
break;
}
path_lost = 0;
parent = orig_parent;
while (*cur_exp < exp->used) {
switch (exp->tokens[*cur_exp]) {
case LYXP_TOKEN_DOT:
case LYXP_TOKEN_DDOT:
case LYXP_TOKEN_NAMETEST:
if (path_lost) {
/* we do not know anything anymore, just copy it */
if (ly_path_data2schema_copy_token(ctx, exp, *cur_exp, out, out_used)) {
goto error;
}
break;
}
str = strndup(exp->expr + exp->expr_pos[*cur_exp], exp->tok_len[*cur_exp]);
LY_CHECK_ERR_GOTO(!str, LOGMEM(ctx), error);
col = strchr(str, ':');
if (col) {
*col = '\0';
++col;
}
/* first node */
if (first) {
if (!col) {
LOGVAL(ctx, LYE_PATH_MISSMOD, LY_VLOG_NONE, NULL);
goto error;
}
cur_mod = ly_ctx_get_module(ctx, str, NULL, 0);
if (!cur_mod) {
LOGVAL(ctx, LYE_PATH_INMOD, LY_VLOG_STR, str);
goto error;
}
first = 0;
}
if (((col ? col[0] : str[0]) == '.') || ((col ? col[0] : str[0]) == '*')) {
free(str);
str = NULL;
if (end_token) {
LOGERR(ctx, LY_EINVAL, "Invalid path used (%s in a subexpression).", str);
goto error;
}
/* we can no longer evaluate the path, so just copy the rest */
path_lost = 1;
if (ly_path_data2schema_copy_token(ctx, exp, *cur_exp, out, out_used)) {
goto error;
}
break;
}
/* create schema path for this data node */
node = NULL;
while ((node = lys_getnext(node, parent, cur_mod, LYS_GETNEXT_NOSTATECHECK))) {
if (strcmp(node->name, col ? col : str)) {
continue;
}
if (col && strcmp(lys_node_module(node)->name, str)) {
continue;
}
if (!col && (lys_node_module(node) != lys_node_module(parent))) {
continue;
}
/* determine how deep the node actually is, we must generate the path from the highest parent */
j = 0;
node2 = node;
while (node2 != parent) {
node2 = lys_parent(node2);
if (!node2 || (node2->nodetype != LYS_USES)) {
++j;
}
}
/* first node, do not print '/' */
slash = 0;
while (j) {
k = j - 1;
node2 = node;
while (k) {
node2 = lys_parent(node2);
assert(node2);
if (node2->nodetype != LYS_USES) {
--k;
}
}
if ((lys_node_module(node2) != cur_mod) || !parent) {
/* module name and node name */
len = slash + strlen(lys_node_module(node2)->name) + 1 + strlen(node2->name);
*out = ly_realloc(*out, *out_used + len);
LY_CHECK_ERR_GOTO(!(*out), LOGMEM(ctx), error);
sprintf(*out + *out_used - 1, "%s%s:%s", slash ? "/" : "", lys_node_module(node2)->name, node2->name);
*out_used += len;
} else {
/* only node name */
len = slash + strlen(node2->name);
*out = ly_realloc(*out, *out_used + len);
LY_CHECK_ERR_GOTO(!(*out), LOGMEM(ctx), error);
sprintf(*out + *out_used - 1, "%s%s", slash ? "/" : "", node2->name);
*out_used += len;
}
slash = 1;
--j;
}
break;
}
if (!node) {
LOGVAL(ctx, LYE_PATH_INNODE, LY_VLOG_STR, col ? col : str);
goto error;
}
/* copy any whitespaces */
for (len = 0; isspace(exp->expr[exp->expr_pos[*cur_exp] + exp->tok_len[*cur_exp] + len]); ++len);
if (len) {
*out = ly_realloc(*out, *out_used + len);
LY_CHECK_ERR_GOTO(!(*out), LOGMEM(ctx), error);
sprintf(*out + *out_used - 1, "%*s", len, " ");
*out_used += len;
}
/* next iteration */
free(str);
str = NULL;
parent = node;
break;
case LYXP_TOKEN_COMMA:
case LYXP_TOKEN_OPERATOR_LOG:
case LYXP_TOKEN_OPERATOR_COMP:
case LYXP_TOKEN_OPERATOR_MATH:
case LYXP_TOKEN_OPERATOR_UNI:
/* reset the processing */
first = 1;
path_lost = 0;
parent = orig_parent;
/* fallthrough */
case LYXP_TOKEN_OPERATOR_PATH:
if ((exp->tokens[*cur_exp] == LYXP_TOKEN_OPERATOR_PATH) && (exp->tok_len[*cur_exp] == 2)) {
/* we can no longer evaluate the path further */
path_lost = 1;
}
/* fallthrough */
case LYXP_TOKEN_NODETYPE:
case LYXP_TOKEN_FUNCNAME:
case LYXP_TOKEN_LITERAL:
case LYXP_TOKEN_NUMBER:
/* just copy it */
if (ly_path_data2schema_copy_token(ctx, exp, *cur_exp, out, out_used)) {
goto error;
}
break;
case LYXP_TOKEN_BRACK1:
case LYXP_TOKEN_PAR1:
if (ly_path_data2schema_subexp(ctx, parent, cur_mod, exp, cur_exp, out, out_used)) {
goto error;
}
break;
default:
if (end_token && (exp->tokens[*cur_exp] == end_token)) {
/* we are done (with this subexpression) */
if (ly_path_data2schema_copy_token(ctx, exp, *cur_exp, out, out_used)) {
goto error;
}
return 0;
}
LOGERR(ctx, LY_EINVAL, "Invalid token used (%.*s).", exp->tok_len[*cur_exp], exp->expr + exp->expr_pos[*cur_exp]);
goto error;
}
++(*cur_exp);
}
if (end_token) {
LOGVAL(ctx, LYE_XPATH_EOF, LY_VLOG_NONE, NULL);
return -1;
}
return 0;
error:
free(str);
return -1;
}
API char *
ly_path_data2schema(struct ly_ctx *ctx, const char *data_path)
{
struct lyxp_expr *exp;
uint16_t out_used, cur_exp = 0;
char *out;
int r, mod_name_len, nam_len, is_relative = -1;
const char *mod_name, *name;
const struct lys_module *mod = NULL;
const struct lys_node *parent = NULL;
char *str;
if (!ctx || !data_path) {
LOGARG;
return NULL;
}
if ((r = parse_schema_nodeid(data_path, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 1)) < 1) {
LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, data_path[-r], &data_path[-r]);
return NULL;
}
if (name[0] == '#') {
if (is_relative) {
LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, '#', name);
return NULL;
}
++name;
--nam_len;
if (!mod_name) {
str = strndup(data_path, (name + nam_len) - data_path);
LOGVAL(ctx, LYE_PATH_MISSMOD, LY_VLOG_STR, str);
free(str);
return NULL;
}
str = strndup(mod_name, mod_name_len);
if (!str) {
LOGMEM(ctx);
return NULL;
}
mod = ly_ctx_get_module(ctx, str, NULL, 1);
free(str);
if (!mod) {
str = strndup(data_path, (mod_name + mod_name_len) - data_path);
LOGVAL(ctx, LYE_PATH_INMOD, LY_VLOG_STR, str);
free(str);
return NULL;
}
parent = lyp_get_yang_data_template(mod, name, nam_len);
if (!parent) {
str = strndup(data_path, (name + nam_len) - data_path);
LOGVAL(ctx, LYE_PATH_INNODE, LY_VLOG_STR, str);
free(str);
return NULL;
}
out_used = (name + nam_len) - data_path + 1;
out = malloc(out_used);
LY_CHECK_ERR_RETURN(!out, LOGMEM(ctx), NULL);
memcpy(out, data_path, out_used -1);
data_path += r;
} else {
out_used = 1;
out = malloc(1);
LY_CHECK_ERR_RETURN(!out, LOGMEM(ctx), NULL);
}
exp = lyxp_parse_expr(ctx, data_path);
if (!exp) {
free(out);
return NULL;
}
if (parent) {
if (ly_path_data2schema_subexp(ctx, parent, mod, exp, &cur_exp, &out, &out_used)) {
free(out);
out = NULL;
}
} else {
if (ly_path_data2schema_subexp(ctx, NULL, NULL, exp, &cur_exp, &out, &out_used)) {
free(out);
out = NULL;
}
}
lyxp_expr_free(exp);
return out;
}
int
ly_new_node_validity(const struct lys_node *schema)
{
int validity;
validity = LYD_VAL_OK;
if (schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
if (((struct lys_node_leaf *)schema)->type.base == LY_TYPE_LEAFREF) {
/* leafref target validation */
validity |= LYD_VAL_LEAFREF;
}
}
if (schema->nodetype & (LYS_LEAFLIST | LYS_LIST)) {
/* duplicit instance check */
validity |= LYD_VAL_DUP;
}
if ((schema->nodetype == LYS_LIST) && ((struct lys_node_list *)schema)->unique_size) {
/* unique check */
validity |= LYD_VAL_UNIQUE;
}
if (schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CONTAINER | LYS_NOTIF | LYS_RPC | LYS_ACTION | LYS_ANYDATA)) {
/* mandatory children check */
validity |= LYD_VAL_MAND;
}
return validity;
}
void *
ly_realloc(void *ptr, size_t size)
{
void *new_mem;
new_mem = realloc(ptr, size);
if (!new_mem) {
free(ptr);
}
return new_mem;
}
int
ly_strequal_(const char *s1, const char *s2)
{
if (s1 == s2) {
return 1;
} else if (!s1 || !s2) {
return 0;
} else {
for ( ; *s1 == *s2; s1++, s2++) {
if (*s1 == '\0') {
return 1;
}
}
return 0;
}
}
int64_t
dec_pow(uint8_t exp)
{
int64_t ret = 1;
uint8_t i;
for (i = 0; i < exp; ++i) {
ret *= 10;
}
return ret;
}
int
dec64cmp(int64_t num1, uint8_t dig1, int64_t num2, uint8_t dig2)
{
if (dig1 < dig2) {
num2 /= dec_pow(dig2 - dig1);
} else if (dig1 > dig2) {
num1 /= dec_pow(dig1 - dig2);
}
if (num1 == num2) {
return 0;
}
return (num1 > num2 ? 1 : -1);
}
LYB_HASH
lyb_hash(struct lys_node *sibling, uint8_t collision_id)
{
struct lys_module *mod;
int ext_len;
uint32_t full_hash;
LYB_HASH hash;
#ifdef LY_ENABLED_CACHE
if ((collision_id < LYS_NODE_HASH_COUNT) && sibling->hash[collision_id]) {
return sibling->hash[collision_id];
}
#endif
mod = lys_node_module(sibling);
full_hash = dict_hash_multi(0, mod->name, strlen(mod->name));
full_hash = dict_hash_multi(full_hash, sibling->name, strlen(sibling->name));
if (collision_id) {
if (collision_id > strlen(mod->name)) {
/* fine, we will not hash more bytes, just use more bits from the hash than previously */
ext_len = strlen(mod->name);
} else {
/* use one more byte from the module name than before */
ext_len = collision_id;
}
full_hash = dict_hash_multi(full_hash, mod->name, ext_len);
}
full_hash = dict_hash_multi(full_hash, NULL, 0);
/* use the shortened hash */
hash = full_hash & (LYB_HASH_MASK >> collision_id);
/* add colision identificator */
hash |= LYB_HASH_COLLISION_ID >> collision_id;
/* save this hash */
#ifdef LY_ENABLED_CACHE
if (collision_id < LYS_NODE_HASH_COUNT) {
sibling->hash[collision_id] = hash;
}
#endif
return hash;
}
int
lyb_has_schema_model(struct lys_node *sibling, const struct lys_module **models, int mod_count)
{
int i;
const struct lys_module *mod = lys_node_module(sibling);
for (i = 0; i < mod_count; ++i) {
if (mod == models[i]) {
return 1;
}
}
return 0;
}