| /** |
| * @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; |
| |
| 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 */ |
| 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; |
| } |
| |
| 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, "/ [\'\"", (col - in) - 1); |
| if ((id[0] == '/') || (id[0] == ' ') || (id[0] == '[') || (id[0] == '\'') || (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_INMOD_LEN, LY_VLOG_NONE, NULL, 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) || (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; |
| switch (schema->nodetype) { |
| case LYS_LEAF: |
| case LYS_LEAFLIST: |
| if (((struct lys_node_leaf *)schema)->type.base == LY_TYPE_LEAFREF) { |
| validity |= LYD_VAL_LEAFREF; |
| } |
| validity |= LYD_VAL_MAND; |
| break; |
| case LYS_LIST: |
| validity |= LYD_VAL_UNIQUE; |
| /* fallthrough */ |
| case LYS_CONTAINER: |
| case LYS_NOTIF: |
| case LYS_RPC: |
| case LYS_ACTION: |
| case LYS_ANYXML: |
| case LYS_ANYDATA: |
| validity |= LYD_VAL_MAND; |
| break; |
| default: |
| break; |
| } |
| |
| 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); |
| } |