1
0
Fork 0

Merging upstream version 3.7.8.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-05 08:08:50 +01:00
parent 099007bbc4
commit a3c6363c26
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
52 changed files with 13518 additions and 998 deletions

View file

@ -1,39 +0,0 @@
name: "CodeQL"
on:
push:
branches: [ "master", "devel" ]
pull_request:
branches: [ "devel" ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ cpp ]
steps:
- name: Checkout
uses: actions/checkout@main
- name: Initialize CodeQL
uses: github/codeql-action/init@main
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@main
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@main
with:
category: "/language:${{ matrix.language }}"

View file

@ -59,14 +59,14 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
# set version of the project
set(LIBYANG_MAJOR_VERSION 3)
set(LIBYANG_MINOR_VERSION 4)
set(LIBYANG_MICRO_VERSION 2)
set(LIBYANG_MINOR_VERSION 7)
set(LIBYANG_MICRO_VERSION 8)
set(LIBYANG_VERSION ${LIBYANG_MAJOR_VERSION}.${LIBYANG_MINOR_VERSION}.${LIBYANG_MICRO_VERSION})
# set version of the library
set(LIBYANG_MAJOR_SOVERSION 3)
set(LIBYANG_MINOR_SOVERSION 4)
set(LIBYANG_MICRO_SOVERSION 2)
set(LIBYANG_MINOR_SOVERSION 6)
set(LIBYANG_MICRO_SOVERSION 10)
set(LIBYANG_SOVERSION_FULL ${LIBYANG_MAJOR_SOVERSION}.${LIBYANG_MINOR_SOVERSION}.${LIBYANG_MICRO_SOVERSION})
set(LIBYANG_SOVERSION ${LIBYANG_MAJOR_SOVERSION})

View file

@ -45,7 +45,7 @@ macro(USE_COMPAT)
find_package(Threads)
list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
check_function_exists(pthread_mutex_timedlock HAVE_PTHREAD_MUTEX_TIMEDLOCK)
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} "") # cmake <= 3.19 needs at least one item
test_big_endian(IS_BIG_ENDIAN)

30
SECURITY.md Normal file
View file

@ -0,0 +1,30 @@
# Security Policy
If you discover a security-related issue (a crash), please report it based on the instructions below.
## Reporting a Vulnerability
Please **DO NOT** file a public issue, instead report the vulnerability on the relevant
[GitHub security](https://github.com/CESNET/libyang/security) page. If you do not receive any reaction within 48 hours,
please also send an email to [mvasko@cesnet.cz].
## Review Process
After receiving the report, an initial triage and technical analysis is performed to confirm the report and determine
its scope. We may request additional information in this stage of the process.
Once a reviewer has confirmed the relevance of the report, a draft security advisory will be created on GitHub. The
draft advisory will be used to discuss the issue with maintainers, the reporter(s), and where applicable, other affected
parties under embargo.
If the vulnerability is accepted, a timeline for developing a patch, public disclosure, and patch release will be
determined. If there is an embargo period on public disclosure before the patch release, the reporter(s) are expected to
participate in the discussion of the timeline and abide by agreed upon dates for public disclosure.
Usually, the reasonably complex issues are fixed within hours of being reported.
## Supported Versions
After an issue is fixed, it **WILL NOT** be backported to any released version. Instead, it is kept in the public `devel`
branch, which is periodically merged into the main branch when a new release is due. So, the issue will be fixed in the
next release after it is fixed.

7645
doc/compat_report_1_2.html Normal file

File diff suppressed because it is too large Load diff

3221
doc/compat_report_2_3.html Normal file

File diff suppressed because it is too large Load diff

BIN
doc/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -128,12 +128,16 @@ lyd_diff_add_create_nested_userord(struct lyd_node *node)
meta_name = "yang:position";
pos = lyd_list_pos(node);
if (asprintf(&dyn, "%" PRIu32, pos) == -1) {
LOGMEM(LYD_CTX(node));
rc = LY_EMEM;
goto cleanup;
if (pos > 1) {
if (asprintf(&dyn, "%" PRIu32, pos - 1) == -1) {
LOGMEM(LYD_CTX(node));
rc = LY_EMEM;
goto cleanup;
}
meta_val = dyn;
} else {
meta_val = "";
}
meta_val = dyn;
} else if (node->schema->nodetype == LYS_LIST) {
meta_name = "yang:key";

View file

@ -264,7 +264,6 @@ lyht_find_rec(const struct ly_ht *ht, void *val_p, uint32_t hash, ly_bool mod, l
if (col) {
*col = 0;
}
*rec_p = NULL;
LYHT_ITER_HLIST_RECS(ht, hlist_idx, rec_idx, rec) {
if ((rec->hash == hash) && val_equal(val_p, &rec->val, mod, ht->cb_data)) {
@ -278,6 +277,7 @@ lyht_find_rec(const struct ly_ht *ht, void *val_p, uint32_t hash, ly_bool mod, l
}
/* not found even in collisions */
*rec_p = NULL;
return LY_ENOTFOUND;
}
@ -286,14 +286,10 @@ lyht_find(const struct ly_ht *ht, void *val_p, uint32_t hash, void **match_p)
{
struct ly_ht_rec *rec;
if (match_p) {
*match_p = NULL;
}
lyht_find_rec(ht, val_p, hash, 0, ht->val_equal, NULL, &rec);
if (rec && match_p) {
*match_p = rec->val;
if (match_p) {
*match_p = rec ? rec->val : NULL;
}
return rec ? LY_SUCCESS : LY_ENOTFOUND;
}
@ -313,14 +309,14 @@ lyht_find_with_val_cb(const struct ly_ht *ht, void *val_p, uint32_t hash, lyht_v
LIBYANG_API_DEF LY_ERR
lyht_find_next_with_collision_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
lyht_value_equal_cb collision_val_equal, void **match_p)
lyht_value_equal_cb val_equal, void **match_p)
{
struct ly_ht_rec *rec;
uint32_t rec_idx;
uint32_t i;
/* find the record of the previously found value */
if (lyht_find_rec(ht, val_p, hash, 1, ht->val_equal, &i, &rec)) {
if (lyht_find_rec(ht, val_p, hash, 1, val_equal ? val_equal : ht->val_equal, &i, &rec)) {
/* not found, cannot happen */
LOGINT_RET(NULL);
}
@ -333,8 +329,8 @@ lyht_find_next_with_collision_cb(const struct ly_ht *ht, void *val_p, uint32_t h
continue;
}
if (collision_val_equal) {
if (collision_val_equal(val_p, &rec->val, 0, ht->cb_data)) {
if (val_equal) {
if (val_equal(val_p, &rec->val, 0, ht->cb_data)) {
/* even the value matches */
if (match_p) {
*match_p = rec->val;

View file

@ -163,13 +163,14 @@ LIBYANG_API_DECL LY_ERR lyht_find_next(const struct ly_ht *ht, void *val_p, uint
* @param[in] ht Hash table to search in.
* @param[in] val_p Pointer to the previously found value in @p ht.
* @param[in] hash Hash of the previously found value.
* @param[in] collision_val_equal Val equal callback to use for checking collisions.
* @param[in] val_equal Callback for checking value equivalence. Called with @p mod 1 when searching for the first value
* and then uses @p mod 0 to check all the following collisions.
* @param[out] match_p Pointer to the matching value, optional.
* @return LY_SUCCESS if value was found,
* @return LY_ENOTFOUND if not found.
*/
LIBYANG_API_DECL LY_ERR lyht_find_next_with_collision_cb(const struct ly_ht *ht, void *val_p, uint32_t hash,
lyht_value_equal_cb collision_val_equal, void **match_p);
lyht_value_equal_cb val_equal, void **match_p);
/**
* @brief Insert a value into a hash table.

View file

@ -381,19 +381,19 @@ ly_get_log_clb(void)
}
void
ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnode, const char *spath, const struct ly_in *in)
ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnode, const char *path, const struct ly_in *in)
{
if (scnode) {
ly_set_add(&log_location.scnodes, (void *)scnode, 1, NULL);
}
if (dnode || (!scnode && !spath && !in)) {
if (dnode || (!scnode && !path && !in)) {
ly_set_add(&log_location.dnodes, (void *)dnode, 1, NULL);
}
if (spath) {
char *s = strdup(spath);
if (path) {
char *s = strdup(path);
LY_CHECK_ERR_RET(!s, LOGMEM(NULL), );
ly_set_add(&log_location.spaths, s, 1, NULL);
ly_set_add(&log_location.paths, s, 1, NULL);
}
if (in) {
ly_set_add(&log_location.inputs, (void *)in, 1, NULL);
@ -401,7 +401,7 @@ ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnode, co
}
void
ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t spath_steps, uint32_t in_steps)
ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t path_steps, uint32_t in_steps)
{
for (uint32_t i = scnode_steps; i && log_location.scnodes.count; i--) {
log_location.scnodes.count--;
@ -411,8 +411,8 @@ ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t spa
log_location.dnodes.count--;
}
for (uint32_t i = spath_steps; i && log_location.spaths.count; i--) {
ly_set_rm_index(&log_location.spaths, log_location.spaths.count - 1, free);
for (uint32_t i = path_steps; i && log_location.paths.count; i--) {
ly_set_rm_index(&log_location.paths, log_location.paths.count - 1, free);
}
for (uint32_t i = in_steps; i && log_location.inputs.count; i--) {
@ -426,8 +426,8 @@ ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t spa
if (dnode_steps && !log_location.dnodes.count) {
ly_set_erase(&log_location.dnodes, NULL);
}
if (spath_steps && !log_location.spaths.count) {
ly_set_erase(&log_location.spaths, free);
if (path_steps && !log_location.paths.count) {
ly_set_erase(&log_location.paths, free);
}
if (in_steps && !log_location.inputs.count) {
ly_set_erase(&log_location.inputs, NULL);
@ -808,21 +808,38 @@ cleanup:
static LY_ERR
ly_vlog_build_path_line(const struct ly_ctx *ctx, char **data_path, char **schema_path, uint64_t *line)
{
int r;
char *path;
*data_path = NULL;
*schema_path = NULL;
*line = 0;
if (log_location.spaths.count && ((const char *)(log_location.spaths.objs[log_location.spaths.count - 1]))[0]) {
/* simply get what is in the provided path string */
*schema_path = strdup(log_location.spaths.objs[log_location.spaths.count - 1]);
/* data/schema node */
if (log_location.dnodes.count) {
LY_CHECK_RET(ly_vlog_build_data_path(ctx, data_path));
} else if (log_location.scnodes.count) {
*schema_path = lysc_path(log_location.scnodes.objs[log_location.scnodes.count - 1], LYSC_PATH_LOG, NULL, 0);
LY_CHECK_ERR_RET(!*schema_path, LOGMEM(ctx), LY_EMEM);
} else {
/* data/schema node */
if (log_location.dnodes.count) {
LY_CHECK_RET(ly_vlog_build_data_path(ctx, data_path));
} else if (log_location.scnodes.count) {
*schema_path = lysc_path(log_location.scnodes.objs[log_location.scnodes.count - 1], LYSC_PATH_LOG, NULL, 0);
LY_CHECK_ERR_RET(!*schema_path, LOGMEM(ctx), LY_EMEM);
}
if (log_location.paths.count && ((const char *)(log_location.paths.objs[log_location.paths.count - 1]))[0]) {
/* append the provided path string to data/schema path, if any */
if (*data_path) {
r = asprintf(&path, "%s%s", *data_path, (char *)log_location.paths.objs[log_location.paths.count - 1]);
} else if (*schema_path) {
r = asprintf(&path, "%s%s", *schema_path, (char *)log_location.paths.objs[log_location.paths.count - 1]);
} else {
r = asprintf(&path, "%s", (char *)log_location.paths.objs[log_location.paths.count - 1]);
}
LY_CHECK_ERR_RET(r == -1, LOGMEM(ctx), LY_EMEM);
if (*data_path) {
free(*data_path);
*data_path = path;
} else {
free(*schema_path);
*schema_path = path;
}
}

View file

@ -75,7 +75,7 @@ struct ly_log_location_s {
struct ly_set inputs; /**< Set of const struct ly_in *in pointers providing the input handler with the line information (LIFO) */
struct ly_set scnodes; /**< Set of const struct lysc_node *scnode pointers providing the compiled schema node to generate path (LIFO) */
struct ly_set dnodes; /**< Set of const struct lyd_node *dnode pointers providing the data node to generate path (LIFO) */
struct ly_set spaths; /**< Set of schema path strings (LIFO) */
struct ly_set paths; /**< Set of path strings (LIFO) */
};
/**
@ -122,21 +122,21 @@ void ly_err_move(struct ly_ctx *src_ctx, struct ly_ctx *trg_ctx);
*
* @param[in] scnode Compiled schema node.
* @param[in] dnode Data node.
* @param[in] spath Direct schema path string to print.
* @param[in] path Direct path string to append and print.
* @param[in] in Input handler (providing line number).
*/
void ly_log_location(const struct lysc_node *scnode, const struct lyd_node *dnode,
const char *spath, const struct ly_in *in);
const char *path, const struct ly_in *in);
/**
* @brief Revert the specific logger location data by number of changes made by ::ly_log_location().
*
* @param[in] scnode_steps Number of items in ::ly_log_location_s.scnodes to forget.
* @param[in] dnode_steps Number of items in ::ly_log_location_s.dnodes to forget.
* @param[in] spath_steps Number of path strings in ::ly_log_location_s.spaths to forget.
* @param[in] path_steps Number of path strings in ::ly_log_location_s.paths to forget.
* @param[in] in_steps Number of input handlers ::ly_log_location_s.inputs to forget.
*/
void ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t spath_steps, uint32_t in_steps);
void ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t path_steps, uint32_t in_steps);
/**
* @brief Get the stored data node for logging at the index.

View file

@ -54,6 +54,7 @@
#include "tree_data_internal.h"
#include "tree_schema.h"
#include "tree_schema_internal.h"
#include "validation.h"
void
lyd_ctx_free(struct lyd_ctx *lydctx)
@ -63,6 +64,7 @@ lyd_ctx_free(struct lyd_ctx *lydctx)
ly_set_erase(&lydctx->node_when, NULL);
ly_set_erase(&lydctx->ext_node, free);
ly_set_erase(&lydctx->ext_val, free);
lyd_val_getnext_ht_free(lydctx->val_getnext_ht);
}
LY_ERR
@ -321,7 +323,7 @@ lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct l
void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node)
{
LY_ERR rc = LY_SUCCESS;
char *dpath = NULL, *path = NULL;
char *path = NULL;
ly_bool incomplete;
struct lyd_meta *first = NULL;
ly_bool store_only = (lydctx->parse_opts & LYD_PARSE_STORE_ONLY) == LYD_PARSE_STORE_ONLY ? 1 : 0;
@ -332,8 +334,7 @@ lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct l
}
/* generate path to the metadata */
LY_CHECK_RET(ly_vlog_build_data_path(lydctx->data_ctx->ctx, &dpath));
if (asprintf(&path, "%s/@%s:%.*s", dpath, mod->name, (int)name_len, name) == -1) {
if (asprintf(&path, "/@%s:%.*s", mod->name, (int)name_len, name) == -1) {
LOGMEM(lydctx->data_ctx->ctx);
rc = LY_EMEM;
goto cleanup;
@ -354,13 +355,12 @@ lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, struct l
cleanup:
ly_log_location_revert(0, 0, 1, 0);
free(dpath);
free(path);
return rc;
}
LY_ERR
lyd_parse_check_keys(struct lyd_node *node)
lyd_parser_check_keys(struct lyd_node *node)
{
const struct lysc_node *skey = NULL;
const struct lyd_node *key;
@ -381,7 +381,7 @@ lyd_parse_check_keys(struct lyd_node *node)
}
LY_ERR
lyd_parse_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct lyd_ctx *lydctx,
lyd_parser_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct lyd_ctx *lydctx,
struct lysc_ext_instance *ext)
{
struct lyd_meta *meta2, *prev_meta = NULL, *next_meta = NULL;
@ -420,7 +420,7 @@ lyd_parse_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct l
}
/* update dflt flag for all parent NP containers */
lyd_cont_set_dflt(lyd_parent(node));
lyd_np_cont_dflt_set(lyd_parent(node));
break;
}
@ -444,6 +444,37 @@ lyd_parse_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct l
return LY_SUCCESS;
}
LY_ERR
lyd_parser_validate_new_implicit(struct lyd_ctx *lydctx, struct lyd_node *node)
{
LY_ERR r, rc = LY_SUCCESS;
if (lyd_owner_module(node) != lydctx->val_getnext_ht_mod) {
/* free any previous getnext HT */
lyd_val_getnext_ht_free(lydctx->val_getnext_ht);
/* create the getnext HT for this module */
r = lyd_val_getnext_ht_new(&lydctx->val_getnext_ht);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
lydctx->val_getnext_ht_mod = lyd_owner_module(node);
}
/* new node validation, autodelete CANNOT occur (it can if multi-error), all nodes are new */
r = lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, NULL, lydctx->val_opts, lydctx->int_opts,
lydctx->val_getnext_ht, NULL);
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
/* add any missing default children */
r = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
&lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0,
lydctx->val_getnext_ht, NULL);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
cleanup:
return rc;
}
void
lys_parser_fill_filepath(struct ly_ctx *ctx, struct ly_in *in, const char **filepath)
{
@ -3569,7 +3600,7 @@ lysp_stmt_parse(struct lysp_ctx *pctx, const struct lysp_stmt *stmt, void **resu
}
LY_ERR
lys_parse_ext_instance_stmt(struct lysp_ctx *pctx, struct lysp_ext_substmt *substmt, struct lysp_stmt *stmt)
lys_parser_ext_instance_stmt(struct lysp_ctx *pctx, struct lysp_ext_substmt *substmt, struct lysp_stmt *stmt)
{
LY_ERR rc = LY_SUCCESS;

View file

@ -332,29 +332,29 @@ LIBYANG_API_DECL LY_ERR lyd_parse_ext_data(const struct lysc_ext_instance *ext,
* @{
*/
enum lyd_type {
LYD_TYPE_DATA_YANG = 0, /* generic YANG instance data */
LYD_TYPE_RPC_YANG, /* instance of a YANG RPC/action request with only "input" data children,
including all parents and optional top-level "action" element in case of an action */
LYD_TYPE_NOTIF_YANG, /* instance of a YANG notification, including all parents in case of a nested one */
LYD_TYPE_REPLY_YANG, /* instance of a YANG RPC/action reply with only "output" data children,
including all parents in case of an action */
LYD_TYPE_DATA_YANG = 0, /**< generic YANG instance data */
LYD_TYPE_RPC_YANG, /**< instance of a YANG RPC/action request with only "input" data children,
including all parents and optional top-level "action" element in case of an action */
LYD_TYPE_NOTIF_YANG, /**< instance of a YANG notification, including all parents in case of a nested one */
LYD_TYPE_REPLY_YANG, /**< instance of a YANG RPC/action reply with only "output" data children,
including all parents in case of an action */
LYD_TYPE_RPC_NETCONF, /* complete NETCONF RPC invocation as defined for
[RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
[action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
LYD_TYPE_NOTIF_NETCONF, /* complete NETCONF notification message as defined for
[notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
LYD_TYPE_REPLY_NETCONF, /* complete NETCONF RPC reply as defined for
[RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
[action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
LYD_TYPE_RPC_NETCONF, /**< complete NETCONF RPC invocation as defined for
[RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
[action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
LYD_TYPE_NOTIF_NETCONF, /**< complete NETCONF notification message as defined for
[notification](https://tools.ietf.org/html/rfc7950#section-7.16.2) */
LYD_TYPE_REPLY_NETCONF, /**< complete NETCONF RPC reply as defined for
[RPC](https://tools.ietf.org/html/rfc7950#section-7.14.4) and
[action](https://tools.ietf.org/html/rfc7950#section-7.15.2) */
LYD_TYPE_RPC_RESTCONF, /* message-body of a RESTCONF operation input parameters
([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.1)) */
LYD_TYPE_NOTIF_RESTCONF, /* RESTCONF JSON notification data
([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-6.4)), to parse
a notification in XML, use ::LYD_TYPE_NOTIF_NETCONF */
LYD_TYPE_REPLY_RESTCONF /* message-body of a RESTCONF operation output parameters
([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.2)) */
LYD_TYPE_RPC_RESTCONF, /**< message-body of a RESTCONF operation input parameters
([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.1)) */
LYD_TYPE_NOTIF_RESTCONF, /**< RESTCONF JSON notification data
([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-6.4)), to parse
a notification in XML, use ::LYD_TYPE_NOTIF_NETCONF */
LYD_TYPE_REPLY_RESTCONF /**< message-body of a RESTCONF operation output parameters
([ref](https://www.rfc-editor.org/rfc/rfc8040.html#section-3.6.2)) */
};
/** @} datatype */
@ -409,13 +409,21 @@ enum lyd_type {
* - @p op - must be NULL, @p parent points to the operation;
* Note that error reply should be parsed as 'yang-data' extension data.
*
* - ::LYD_TYPE_RPC_YANG:
* - ::LYD_TYPE_NOTIF_YANG:
* - ::LYD_TYPE_REPLY_YANG:
* - all the parameters have their default meaning.
*
* - :: LYD_TYPE_DATA_YANG:
* - not accepted by the function.
*
* @param[in] ctx libyang context.
* @param[in] parent Optional parent to connect the parsed nodes to.
* @param[in] in Input handle to read the input from.
* @param[in] format Expected format of the data in @p in.
* @param[in] data_type Expected operation to parse (@ref datatype).
* @param[out] tree Optional full parsed data tree. If @p parent is set, set to NULL.
* @param[out] op Optional pointer to the operation (action/RPC) node.
* @param[out] op Optional pointer to the operation (action/RPC/notification) node.
* @return LY_ERR value.
* @return LY_ENOT if @p data_type is a NETCONF message and the root XML element is not the expected one.
*/

View file

@ -84,6 +84,8 @@ struct lyd_ctx {
struct ly_set ext_node; /**< set of nodes with extension instances to validate */
struct ly_set ext_val; /**< set of nested subtrees parsed by extensions to validate */
struct lyd_node *op_node; /**< if an RPC/action/notification is being parsed, store the pointer to it */
const struct lys_module *val_getnext_ht_mod; /**< module of the cached schema nodes in getnext HT */
struct ly_ht *val_getnext_ht; /**< cached getnext schema nodes in a HT for validation */
/* callbacks */
lyd_ctx_free_clb free; /**< destructor */
@ -111,6 +113,8 @@ struct lyd_xml_ctx {
struct ly_set ext_node;
struct ly_set ext_val;
struct lyd_node *op_node;
const struct lys_module *val_getnext_ht_mod;
struct ly_ht *val_getnext_ht;
/* callbacks */
lyd_ctx_free_clb free;
@ -134,6 +138,8 @@ struct lyd_json_ctx {
struct ly_set ext_node;
struct ly_set ext_val;
struct lyd_node *op_node;
const struct lys_module *val_getnext_ht_mod;
struct ly_ht *val_getnext_ht;
/* callbacks */
lyd_ctx_free_clb free;
@ -164,6 +170,8 @@ struct lyd_lyb_ctx {
struct ly_set ext_node;
struct ly_set ext_val;
struct lyd_node *op_node;
const struct lys_module *val_getnext_ht_mod;
struct ly_ht *val_getnext_ht;
/* callbacks */
lyd_ctx_free_clb free;
@ -424,7 +432,7 @@ LY_ERR lyd_parser_create_meta(struct lyd_ctx *lydctx, struct lyd_node *parent, s
* @return LY_SUCCESS on success.
* @return LY_ENOT on a missing key.
*/
LY_ERR lyd_parse_check_keys(struct lyd_node *node);
LY_ERR lyd_parser_check_keys(struct lyd_node *node);
/**
* @brief Set data flags for a newly parsed node.
@ -435,9 +443,18 @@ LY_ERR lyd_parse_check_keys(struct lyd_node *node);
* @param[in] ext Extension instance if @p node was parsed for one.
* @return LY_ERR value.
*/
LY_ERR lyd_parse_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct lyd_ctx *lydctx,
LY_ERR lyd_parser_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, struct lyd_ctx *lydctx,
struct lysc_ext_instance *ext);
/**
* @brief Validate a new parsed data node and add its implicit children.
*
* @param[in] lydctx Data parser context.
* @param[in] node Parsed node to validate.
* @return LY_ERR value.
*/
LY_ERR lyd_parser_validate_new_implicit(struct lyd_ctx *lydctx, struct lyd_node *node);
/**
* @brief Parse an instance extension statement.
*
@ -446,6 +463,6 @@ LY_ERR lyd_parse_set_data_flags(struct lyd_node *node, struct lyd_meta **meta, s
* @param[in] stmt Parsed generic statement to process.
* @return LY_ERR value.
*/
LY_ERR lys_parse_ext_instance_stmt(struct lysp_ctx *pctx, struct lysp_ext_substmt *substmt, struct lysp_stmt *stmt);
LY_ERR lys_parser_ext_instance_stmt(struct lysp_ctx *pctx, struct lysp_ext_substmt *substmt, struct lysp_stmt *stmt);
#endif /* LY_PARSER_INTERNAL_H_ */

View file

@ -501,6 +501,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
LY_ERR ret = LY_SUCCESS;
struct lyjson_ctx *jsonctx = lydctx->jsonctx;
enum LYJSON_PARSER_STATUS status;
uint32_t *prev_lo, temp_lo = 0;
assert(snode);
@ -523,9 +524,12 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno
if ((ret = lydjson_value_type_hint(jsonctx, &status, type_hint_p))) {
break;
}
prev_lo = ly_temp_log_options(&temp_lo);
if (ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, *type_hint_p)) {
ret = LY_ENOT;
}
ly_temp_log_options(prev_lo);
break;
case LYS_LIST:
/* lists may not have all its keys */
@ -671,7 +675,7 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p)
}
/* add/correct flags */
ret = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, ext);
ret = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, ext);
LY_CHECK_GOTO(ret, cleanup);
break;
}
@ -857,7 +861,7 @@ next_entry:
LY_CHECK_GOTO(rc, cleanup);
/* add/correct flags */
rc = lyd_parse_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL);
rc = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL);
LY_CHECK_GOTO(rc, cleanup);
} else {
/* create attribute */
@ -1434,19 +1438,14 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node
if (snode->nodetype == LYS_LIST) {
/* check all keys exist */
r = lyd_parse_check_keys(*node);
r = lyd_parser_check_keys(*node);
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
}
if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) {
/* new node validation, autodelete CANNOT occur (it can if multi-error), all nodes are new */
r = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, lydctx->val_opts, NULL);
/* new node validation */
r = lyd_parser_validate_new_implicit((struct lyd_ctx *)lydctx, *node);
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
/* add any missing default children */
r = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
&lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
cleanup:
@ -1530,7 +1529,7 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str
LY_CHECK_GOTO(!*node, cleanup);
/* add/correct flags */
r = lyd_parse_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext);
r = lyd_parser_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {

View file

@ -1079,29 +1079,21 @@ lyb_create_term(struct lyd_lyb_ctx *lybctx, const struct lysc_node *snode, struc
* @brief Validate inner node, autodelete default values nad create implicit nodes.
*
* @param[in,out] lybctx LYB context.
* @param[in] snode Schema of the inner node.
* @param[in] node Parsed inner node.
* @return LY_ERR value.
*/
static LY_ERR
lyb_validate_node_inner(struct lyd_lyb_ctx *lybctx, const struct lysc_node *snode, struct lyd_node *node)
lyb_validate_node_inner(struct lyd_lyb_ctx *lybctx, struct lyd_node *node)
{
LY_ERR ret = LY_SUCCESS;
uint32_t impl_opts;
LY_ERR rc = LY_SUCCESS;
if (!(lybctx->parse_opts & LYD_PARSE_ONLY)) {
/* new node validation, autodelete CANNOT occur, all nodes are new */
ret = lyd_validate_new(lyd_node_child_p(node), snode, NULL, 0, NULL);
LY_CHECK_RET(ret);
/* add any missing default children */
impl_opts = (lybctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0;
ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &lybctx->node_when, &lybctx->node_types,
&lybctx->ext_node, impl_opts, NULL);
LY_CHECK_RET(ret);
/* new node validation */
rc = lyd_parser_validate_new_implicit((struct lyd_ctx *)lybctx, node);
LY_CHECK_RET(rc);
}
return ret;
return rc;
}
/**
@ -1324,7 +1316,7 @@ lyb_parse_node_inner(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const
LY_CHECK_GOTO(ret, error);
/* additional procedure for inner node */
ret = lyb_validate_node_inner(lybctx, snode, node);
ret = lyb_validate_node_inner(lybctx, node);
LY_CHECK_GOTO(ret, error);
if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
@ -1463,7 +1455,7 @@ lyb_parse_node_list(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, const s
LY_CHECK_GOTO(ret, error);
/* additional procedure for inner node */
ret = lyb_validate_node_inner(lybctx, snode, node);
ret = lyb_validate_node_inner(lybctx, node);
LY_CHECK_GOTO(ret, error);
if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {

View file

@ -394,8 +394,9 @@ lydxml_data_skip(struct lyxml_ctx *xmlctx)
static LY_ERR
lydxml_data_check_opaq(struct lyd_xml_ctx *lydctx, const struct lysc_node **snode)
{
LY_ERR ret = LY_SUCCESS;
LY_ERR ret = LY_SUCCESS, r;
struct lyxml_ctx *xmlctx = lydctx->xmlctx, pxmlctx;
uint32_t *prev_lo, temp_lo = 0;
if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) {
/* only checks specific to opaque nodes */
@ -420,7 +421,10 @@ lydxml_data_check_opaq(struct lyd_xml_ctx *lydctx, const struct lysc_node **snod
if ((*snode)->nodetype & LYD_NODE_TERM) {
/* value may not be valid in which case we parse it as an opaque node */
if (ly_value_validate(NULL, *snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA)) {
prev_lo = ly_temp_log_options(&temp_lo);
r = ly_value_validate(NULL, *snode, xmlctx->value, xmlctx->value_len, LY_VALUE_XML, &xmlctx->ns, LYD_HINT_DATA);
ly_temp_log_options(prev_lo);
if (r) {
LOGVRB("Parsing opaque term node \"%s\" with invalid value \"%.*s\".", (*snode)->name, (int)xmlctx->value_len,
xmlctx->value);
*snode = NULL;
@ -863,19 +867,14 @@ lydxml_subtree_inner(struct lyd_xml_ctx *lydctx, const struct lysc_node *snode,
if (snode->nodetype == LYS_LIST) {
/* check all keys exist */
r = lyd_parse_check_keys(*node);
r = lyd_parser_check_keys(*node);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) {
/* new node validation, autodelete CANNOT occur (it can if multi-error), all nodes are new */
r = lyd_validate_new(lyd_node_child_p(*node), snode, NULL, lydctx->val_opts, NULL);
/* new node validation */
r = lyd_parser_validate_new_implicit((struct lyd_ctx *)lydctx, *node);
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
/* add any missing default children */
r = lyd_new_implicit_r(*node, lyd_node_child_p(*node), NULL, NULL, &lydctx->node_when, &lydctx->node_types,
&lydctx->ext_node, (lydctx->val_opts & LYD_VALIDATE_NO_STATE) ? LYD_IMPLICIT_NO_STATE : 0, NULL);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
@ -1107,7 +1106,7 @@ lydxml_subtree_r(struct lyd_xml_ctx *lydctx, struct lyd_node *parent, struct lyd
node_parsed:
if (node && snode) {
/* add/correct flags */
r = lyd_parse_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext);
r = lyd_parser_set_data_flags(node, &meta, (struct lyd_ctx *)lydctx, ext);
LY_CHECK_ERR_GOTO(r, rc = r; lyd_free_tree(node), cleanup);
if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) {

View file

@ -736,8 +736,6 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
/* names (keys) are unique - it was checked when parsing */
LOGVAL(ctx, LYVE_XPATH, "Predicate missing for a key of %s \"%s\" in path.",
lys_nodetype2str(ctx_node->nodetype), ctx_node->name);
ly_path_predicates_free(ctx_node->module->ctx, *predicates);
*predicates = NULL;
ret = LY_EVALID;
goto cleanup;
}
@ -813,6 +811,10 @@ ly_path_compile_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_
cleanup:
LOG_LOCBACK(cur_node ? 1 : 0, 0);
if (ret) {
ly_path_predicates_free(ctx_node->module->ctx, *predicates);
*predicates = NULL;
}
return ret;
}

View file

@ -67,7 +67,7 @@ lyplg_ext_parse_extension_instance(struct lysp_ctx *pctx, struct lysp_ext_instan
continue;
}
if ((rc = lys_parse_ext_instance_stmt(pctx, &ext->substmts[u], stmt))) {
if ((rc = lys_parser_ext_instance_stmt(pctx, &ext->substmts[u], stmt))) {
goto cleanup;
}
}

View file

@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Built-in types plugins and interface for user types plugins.
*
* Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
* Copyright (c) 2019 - 2024 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.
@ -526,37 +526,18 @@ decimal:
LIBYANG_API_DEF LY_ERR
lyplg_type_validate_patterns(struct lysc_pattern **patterns, const char *str, size_t str_len, struct ly_err_item **err)
{
int rc, match_opts;
LY_ERR r;
LY_ARRAY_COUNT_TYPE u;
pcre2_match_data *match_data = NULL;
LY_CHECK_ARG_RET(NULL, str, err, LY_EINVAL);
*err = NULL;
LY_ARRAY_FOR(patterns, u) {
/* match_data needs to be allocated each time because of possible multi-threaded evaluation */
match_data = pcre2_match_data_create_from_pattern(patterns[u]->code, NULL);
if (!match_data) {
return ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
}
r = ly_pattern_code_match(patterns[u]->code, str, str_len, err);
LY_CHECK_RET(r && (r != LY_ENOT), r);
match_opts = PCRE2_ANCHORED;
#ifdef PCRE2_ENDANCHORED
/* PCRE2_ENDANCHORED was added in PCRE2 version 10.30 */
match_opts |= PCRE2_ENDANCHORED;
#endif
rc = pcre2_match(patterns[u]->code, (PCRE2_SPTR)str, str_len, 0, match_opts, match_data, NULL);
pcre2_match_data_free(match_data);
if ((rc != PCRE2_ERROR_NOMATCH) && (rc < 0)) {
PCRE2_UCHAR pcre2_errmsg[LY_PCRE2_MSG_LIMIT] = {0};
pcre2_get_error_message(rc, pcre2_errmsg, LY_PCRE2_MSG_LIMIT);
return ly_err_new(err, LY_ESYS, 0, NULL, NULL, "%s", (const char *)pcre2_errmsg);
} else if (((rc == PCRE2_ERROR_NOMATCH) && !patterns[u]->inverted) ||
((rc != PCRE2_ERROR_NOMATCH) && patterns[u]->inverted)) {
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) {
@ -569,6 +550,7 @@ lyplg_type_validate_patterns(struct lysc_pattern **patterns, const char *str, si
}
}
}
return LY_SUCCESS;
}

View file

@ -190,6 +190,7 @@ lyplg_type_store_node_instanceid(const struct ly_ctx *ctx, const struct lysc_typ
if ((((char *)value)[0] == '/') && (value_len == 1)) {
/* special path */
format = LY_VALUE_CANON;
goto store;
}
@ -296,6 +297,34 @@ lyplg_type_print_node_instanceid(const struct ly_ctx *UNUSED(ctx), const struct
return ret;
}
/**
* @brief Implementation of ::lyplg_type_dup_clb for the node-instance-identifier ietf-netconf-acm type.
*/
static LY_ERR
lyplg_type_dup_node_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
{
LY_ERR ret;
memset(dup, 0, sizeof *dup);
/* canonical value */
ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
LY_CHECK_GOTO(ret, error);
if (original->target) {
/* copy path */
ret = ly_path_dup(ctx, original->target, &dup->target);
LY_CHECK_GOTO(ret, error);
} /* else is the special path "/" that has no target stored */
dup->realtype = original->realtype;
return LY_SUCCESS;
error:
lyplg_type_free_instanceid(ctx, dup);
return ret;
}
/**
* @brief Plugin information for instance-identifier type implementation.
*
@ -315,7 +344,7 @@ const struct lyplg_type_record plugins_node_instanceid[] = {
.plugin.compare = lyplg_type_compare_simple,
.plugin.sort = lyplg_type_sort_simple,
.plugin.print = lyplg_type_print_node_instanceid,
.plugin.duplicate = lyplg_type_dup_instanceid,
.plugin.duplicate = lyplg_type_dup_node_instanceid,
.plugin.free = lyplg_type_free_instanceid,
.plugin.lyb_data_len = -1,
},
@ -330,7 +359,7 @@ const struct lyplg_type_record plugins_node_instanceid[] = {
.plugin.compare = lyplg_type_compare_simple,
.plugin.sort = lyplg_type_sort_simple,
.plugin.print = lyplg_type_print_node_instanceid,
.plugin.duplicate = lyplg_type_dup_instanceid,
.plugin.duplicate = lyplg_type_dup_node_instanceid,
.plugin.free = lyplg_type_free_instanceid,
.plugin.lyb_data_len = -1,
},

View file

@ -401,6 +401,9 @@ lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, c
ret = lyb_fill_subvalue(ctx, type_u, value, value_len, prefix_data, subvalue, &options, unres, err);
LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
} else {
/* to correctly resolve the union type, we need to always validate the value */
options &= ~LYPLG_TYPE_STORE_ONLY;
/* store value to subvalue */
ret = union_subvalue_assignment(value, value_len, &subvalue->original, &subvalue->orig_len, &options);
LY_CHECK_GOTO(ret, cleanup);

View file

@ -70,6 +70,18 @@ lyb_ptr_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(c
return 0;
}
/**
* @brief Hash table equal callback for checking collisions.
*
* Implementation of ::lyht_value_equal_cb.
*/
static ly_bool
lyb_col_equal_cb(void *val1_p, void *val2_p, ly_bool mod, void *cb_data)
{
/* for first value check use lyb_ptr_equal_cb, for collisions lyb_hash_equal_cb */
return mod ? lyb_ptr_equal_cb(val1_p, val2_p, mod, cb_data) : lyb_hash_equal_cb(val1_p, val2_p, mod, cb_data);
}
/**
* @brief Check that sibling collision hash is safe to insert into hash table.
*
@ -91,7 +103,6 @@ lyb_hash_sequence_check(struct ly_ht *ht, struct lysc_node *sibling, LYB_HASH ht
return LY_SUCCESS;
}
lyht_set_cb(ht, lyb_ptr_equal_cb);
do {
int64_t j;
@ -103,15 +114,13 @@ lyb_hash_sequence_check(struct ly_ht *ht, struct lysc_node *sibling, LYB_HASH ht
}
if (j == -1) {
/* all whole hash sequences of nodes inserted with last hash col ID compare_col_id collide */
lyht_set_cb(ht, lyb_hash_equal_cb);
return LY_EEXIST;
}
/* get next node inserted with last hash col ID ht_col_id */
} while (!lyht_find_next_with_collision_cb(ht, col_node, lyb_get_hash(*col_node, ht_col_id), lyb_hash_equal_cb,
} while (!lyht_find_next_with_collision_cb(ht, col_node, lyb_get_hash(*col_node, ht_col_id), lyb_col_equal_cb,
(void **)&col_node));
lyht_set_cb(ht, lyb_hash_equal_cb);
return LY_SUCCESS;
}

View file

@ -1056,11 +1056,9 @@ lys_compile_unres_llist_dflts(struct lysc_ctx *ctx, struct lysc_node_leaflist *l
for (u = orig_count; u < LY_ARRAY_COUNT(llist->dflts); ++u) {
for (v = 0; v < u; ++v) {
if (!llist->dflts[u]->realtype->plugin->compare(ctx->ctx, llist->dflts[u], llist->dflts[v])) {
lysc_update_path(ctx, llist->parent ? llist->parent->module : NULL, llist->name);
LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Configuration leaf-list has multiple defaults of the same value \"%s\".",
(char *)llist->dflts[u]->realtype->plugin->print(ctx->ctx, llist->dflts[u], LY_VALUE_CANON,
NULL, NULL, NULL));
lysc_update_path(ctx, NULL, NULL);
return LY_EVALID;
}
}

View file

@ -1244,7 +1244,7 @@ lys_compile_pattern_chblocks_xmlschema2perl(const struct ly_ctx *ctx, const char
}
LY_ERR
lys_compile_type_pattern_check(struct ly_ctx *ctx, const char *pattern, pcre2_code **code)
lys_compile_type_pattern_check(const struct ly_ctx *ctx, const char *pattern, pcre2_code **code)
{
size_t idx, size, brack;
char *perl_regex;
@ -1357,7 +1357,7 @@ lys_compile_type_pattern_check(struct ly_ctx *ctx, const char *pattern, pcre2_co
if (code) {
*code = code_local;
} else {
free(code_local);
pcre2_code_free(code_local);
}
return LY_SUCCESS;
@ -3397,7 +3397,7 @@ lys_compile_node_list(struct lysc_ctx *ctx, struct lysp_node *pnode, struct lysc
if (list->min) {
list->flags |= LYS_MAND_TRUE;
}
list->max = list_p->max ? list_p->max : (uint32_t)-1;
list->max = list_p->max ? list_p->max : UINT32_MAX;
LY_LIST_FOR(list_p->child, child_p) {
LY_CHECK_RET(lys_compile_node(ctx, child_p, node, 0, NULL));

View file

@ -78,7 +78,7 @@ LY_ERR lys_compile_type_range(struct lysc_ctx *ctx, const struct lysp_restr *ran
* @param[in,out] code Compiled PCRE2 pattern. If NULL, the compiled information used to validate pattern are freed.
* @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID.
*/
LY_ERR lys_compile_type_pattern_check(struct ly_ctx *ctx, const char *pattern, pcre2_code **code);
LY_ERR lys_compile_type_pattern_check(const struct ly_ctx *ctx, const char *pattern, pcre2_code **code);
/**
* @brief Compile parsed pattern restriction in conjunction with the patterns from base type.

View file

@ -165,10 +165,17 @@ lyd_parse(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct
}
if (!(parse_opts & LYD_PARSE_ONLY)) {
/* validate data */
r = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
&lydctx->ext_node, &lydctx->ext_val, NULL);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (ext) {
/* special ext instance data validation */
r = lyd_validate_ext(first_p, ext, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
&lydctx->ext_node, &lydctx->ext_val, NULL);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
} else {
/* validate data */
r = lyd_validate(first_p, NULL, ctx, val_opts, 0, &lydctx->node_when, &lydctx->node_types, &lydctx->meta_types,
&lydctx->ext_node, &lydctx->ext_val, NULL);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
}
/* set the operation node */
@ -545,10 +552,9 @@ lyd_insert_get_next_anchor(const struct lyd_node *first_sibling, const struct ly
void
lyd_insert_after_node(struct lyd_node **first_sibling_p, struct lyd_node *sibling, struct lyd_node *node)
{
struct lyd_node_inner *par;
struct lyd_node *first_sibling;
assert(!node->next && (node->prev == node));
assert(!node->next && (node->prev == node) && (sibling != node));
if (sibling->next) {
/* sibling had a succeeding node */
@ -571,20 +577,16 @@ lyd_insert_after_node(struct lyd_node **first_sibling_p, struct lyd_node *siblin
sibling->next = node;
node->parent = sibling->parent;
for (par = node->parent; par; par = par->parent) {
if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) {
/* remove default flags from NP containers */
par->flags &= ~LYD_DEFAULT;
}
if (!(node->flags & LYD_DEFAULT)) {
/* remove default flags from NP containers */
lyd_np_cont_dflt_del(lyd_parent(node));
}
}
void
lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node)
{
struct lyd_node_inner *par;
assert(!node->next && (node->prev == node));
assert(!node->next && (node->prev == node) && (sibling != node));
node->next = sibling;
/* covers situation of sibling being first */
@ -599,11 +601,9 @@ lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node)
}
node->parent = sibling->parent;
for (par = node->parent; par; par = par->parent) {
if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) {
/* remove default flags from NP containers */
par->flags &= ~LYD_DEFAULT;
}
if (!(node->flags & LYD_DEFAULT)) {
/* remove default flags from NP containers */
lyd_np_cont_dflt_del(lyd_parent(node));
}
}
@ -628,11 +628,9 @@ lyd_insert_only_child(struct lyd_node *parent, struct lyd_node *node)
par->child = node;
node->parent = par;
for ( ; par; par = par->parent) {
if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) {
/* remove default flags from NP containers */
par->flags &= ~LYD_DEFAULT;
}
if (!(node->flags & LYD_DEFAULT)) {
/* remove default flags from NP containers */
lyd_np_cont_dflt_del(parent);
}
}
@ -1192,7 +1190,7 @@ lyd_unlink_ignore_lyds(struct lyd_node **first_sibling_p, struct lyd_node *node)
}
/* check for NP container whether its last non-default node is not being unlinked */
lyd_cont_set_dflt(lyd_parent(node));
lyd_np_cont_dflt_set(lyd_parent(node));
node->parent = NULL;
}
@ -1285,9 +1283,8 @@ lyd_insert_meta(struct lyd_node *parent, struct lyd_meta *meta, ly_bool clear_df
}
/* remove default flags from NP containers */
while (clear_dflt && parent && (parent->schema->nodetype == LYS_CONTAINER) && (parent->flags & LYD_DEFAULT)) {
parent->flags &= ~LYD_DEFAULT;
parent = lyd_parent(parent);
if (clear_dflt) {
lyd_np_cont_dflt_del(parent);
}
}
@ -2496,7 +2493,6 @@ lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg,
const struct lyd_node *child_src, *tmp, *sibling_src;
struct lyd_node *match_trg, *dup_src, *elem, *leader;
struct lyd_node_opaq *opaq_trg, *opaq_src;
struct lysc_type *type;
const struct lysc_node *schema;
struct ly_ht *child_dup_inst = NULL;
LY_ERR r;
@ -2546,19 +2542,15 @@ lyd_merge_sibling_r(struct lyd_node **first_trg, struct lyd_node *parent_trg,
&opaq_trg->val_prefix_data);
}
} else if ((match_trg->schema->nodetype == LYS_LEAF) &&
lyd_compare_single(sibling_src, match_trg, LYD_COMPARE_DEFAULTS)) {
/* since they are different, they cannot both be default */
assert(!(sibling_src->flags & LYD_DEFAULT) || !(match_trg->flags & LYD_DEFAULT));
((options & LYD_MERGE_DEFAULTS) || !(sibling_src->flags & LYD_DEFAULT))) {
/* update value */
r = lyd_change_term_val(match_trg, &((struct lyd_node_term *)sibling_src)->value, 0,
sibling_src->flags & LYD_DEFAULT);
LY_CHECK_RET(r && (r != LY_EEXIST) && (r != LY_ENOT), r);
/* update value (or only LYD_DEFAULT flag) only if flag set or the source node is not default */
if ((options & LYD_MERGE_DEFAULTS) || !(sibling_src->flags & LYD_DEFAULT)) {
type = ((struct lysc_node_leaf *)match_trg->schema)->type;
type->plugin->free(LYD_CTX(match_trg), &((struct lyd_node_term *)match_trg)->value);
LY_CHECK_RET(type->plugin->duplicate(LYD_CTX(match_trg), &((struct lyd_node_term *)sibling_src)->value,
&((struct lyd_node_term *)match_trg)->value));
/* copy flags and add LYD_NEW */
match_trg->flags = sibling_src->flags | ((options & LYD_MERGE_WITH_FLAGS) ? 0 : LYD_NEW);
if (options & LYD_MERGE_WITH_FLAGS) {
/* keep the exact same flags */
match_trg->flags = sibling_src->flags;
}
} else if ((match_trg->schema->nodetype & LYS_ANYDATA) && lyd_compare_single(sibling_src, match_trg, 0)) {
/* update value */

View file

@ -26,6 +26,10 @@
# include <sys/socket.h>
# endif
#endif
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include <stddef.h>
#include <stdint.h>
#include <time.h>
@ -2715,6 +2719,33 @@ LIBYANG_API_DECL LY_ERR lyd_leafref_get_links(const struct lyd_node_term *node,
*/
LIBYANG_API_DECL LY_ERR lyd_leafref_link_node_tree(const struct lyd_node *tree);
/**
* @brief Check a string matches an XML Schema regex used in YANG.
*
* @param[in] ctx Optional context for storing errors.
* @param[in] pattern Regular expression pattern to use.
* @param[in] string String to match.
* @param[in] str_len Length of @p string, may be 0 if string is 0-terminated.
* @param[in,out] pcode Optional pointer to PCRE2 code. If set and NULL, it is returned. If set and non-NULL, it is
* used directly for matching instead of compiling @p pattern. Free it using pcre2_code_free().
* @return LY_SUCCESS on a match;
* @return LY_ENOT if the string does not match;
* @return LY_ERR on error.
*/
LIBYANG_API_DECL LY_ERR ly_pattern_match(const struct ly_ctx *ctx, const char *pattern, const char *string,
uint32_t str_len, pcre2_code **pcode);
/**
* @brief Compile an XML Schema regex pattern prior to matching.
*
* @param[in] ctx Optional context for storing errors.
* @param[in] pattern Regular expression pattern to use.
* @param[out] pcode Compiled @p pattern to be used by ::ly_pattern_match(). Free it using pcre2_code_free().
* @return LY_SUCCESS on success;
* @return LY_ERR on error.
*/
LIBYANG_API_DECL LY_ERR ly_pattern_compile(const struct ly_ctx *ctx, const char *pattern, pcre2_code **pcode);
#ifdef __cplusplus
}
#endif

View file

@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Parsing and validation common functions for data trees
*
* Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
* Copyright (c) 2015 - 2024 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.
@ -33,6 +33,7 @@
#include "parser_data.h"
#include "plugins_exts.h"
#include "printer_data.h"
#include "schema_compile_node.h"
#include "set.h"
#include "tree.h"
#include "tree_data.h"
@ -1157,35 +1158,6 @@ lyd_meta_is_internal(const struct lyd_meta *meta)
return 0;
}
void
lyd_cont_set_dflt(struct lyd_node *node)
{
const struct lyd_node *child;
while (node) {
if (!node->schema || (node->flags & LYD_DEFAULT) || !lysc_is_np_cont(node->schema)) {
/* not a non-dflt NP container */
break;
}
LY_LIST_FOR(lyd_child(node), child) {
if (!(child->flags & LYD_DEFAULT)) {
break;
}
}
if (child) {
/* explicit child, no dflt change */
break;
}
/* set the dflt flag */
node->flags |= LYD_DEFAULT;
/* check all parent containers */
node = lyd_parent(node);
}
}
/**
* @brief Comparison callback to match schema node with a schema of a data node.
*
@ -1301,6 +1273,44 @@ lyd_del_move_root(struct lyd_node **root, const struct lyd_node *to_del, const s
}
}
void
lyd_np_cont_dflt_set(struct lyd_node *parent)
{
const struct lyd_node *child;
while (parent) {
if (!parent->schema || (parent->flags & LYD_DEFAULT) || !lysc_is_np_cont(parent->schema)) {
/* not a non-dflt NP container */
break;
}
LY_LIST_FOR(lyd_child(parent), child) {
if (!(child->flags & LYD_DEFAULT)) {
break;
}
}
if (child) {
/* explicit child, no dflt change */
break;
}
/* set the dflt flag */
parent->flags |= LYD_DEFAULT;
/* check all parent containers */
parent = lyd_parent(parent);
}
}
void
lyd_np_cont_dflt_del(struct lyd_node *parent)
{
while (parent && (parent->flags & LYD_DEFAULT)) {
parent->flags &= ~LYD_DEFAULT;
parent = lyd_parent(parent);
}
}
LY_ERR
ly_nested_ext_schema(const struct lyd_node *parent, const struct lysc_node *sparent, const char *prefix,
size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
@ -1834,3 +1844,80 @@ ly_time_ts2str(const struct timespec *ts, char **str)
return ly_time_time2str(ts->tv_sec, ts->tv_nsec ? frac_buf : NULL, str);
}
LY_ERR
ly_pattern_code_match(pcre2_code *pcode, const char *str, size_t str_len, struct ly_err_item **err)
{
int r, match_opts;
pcre2_match_data *match_data = NULL;
/* match_data needs to be allocated each time because of possible multi-threaded evaluation */
match_data = pcre2_match_data_create_from_pattern(pcode, NULL);
if (!match_data) {
return ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
}
match_opts = PCRE2_ANCHORED;
#ifdef PCRE2_ENDANCHORED
/* PCRE2_ENDANCHORED was added in PCRE2 version 10.30 */
match_opts |= PCRE2_ENDANCHORED;
#endif
r = pcre2_match(pcode, (PCRE2_SPTR)str, str_len, 0, match_opts, match_data, NULL);
pcre2_match_data_free(match_data);
if ((r != PCRE2_ERROR_NOMATCH) && (r < 0)) {
PCRE2_UCHAR pcre2_errmsg[LY_PCRE2_MSG_LIMIT] = {0};
pcre2_get_error_message(r, pcre2_errmsg, LY_PCRE2_MSG_LIMIT);
return ly_err_new(err, LY_ESYS, 0, NULL, NULL, "%s", (const char *)pcre2_errmsg);
}
return (r == PCRE2_ERROR_NOMATCH) ? LY_ENOT : LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
ly_pattern_match(const struct ly_ctx *ctx, const char *pattern, const char *string, uint32_t str_len, pcre2_code **pcode)
{
LY_ERR r;
pcre2_code *code;
struct ly_err_item *err = NULL;
LY_CHECK_ARG_RET(ctx, pattern || (pcode && *pcode), string, LY_EINVAL);
if (pcode && *pcode) {
/* use precompiled pattern */
code = *pcode;
} else {
/* compile the pattern */
LY_CHECK_RET(lys_compile_type_pattern_check(ctx, pattern, &code));
}
/* match */
if (!str_len) {
str_len = strlen(string);
}
r = ly_pattern_code_match(code, string, str_len, &err);
if (r && (r != LY_ENOT)) {
ly_err_print(ctx, err);
ly_err_free(err);
return r;
}
if (pcode && !*pcode) {
*pcode = code;
} else if (!pcode || !*pcode) {
pcre2_code_free(code);
}
return r;
}
LIBYANG_API_DEF LY_ERR
ly_pattern_compile(const struct ly_ctx *ctx, const char *pattern, pcre2_code **pcode)
{
LY_CHECK_ARG_RET(ctx, pattern, pcode, LY_EINVAL);
*pcode = NULL;
/* compile the pattern */
return lys_compile_type_pattern_check(ctx, pattern, pcode);
}

View file

@ -4,7 +4,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief internal functions for YANG schema trees.
*
* Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
* Copyright (c) 2015 - 2024 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.
@ -117,13 +117,6 @@ const struct lys_module *lyd_mod_next_module(struct lyd_node *tree, const struct
*/
const struct lys_module *lyd_data_next_module(struct lyd_node **next, struct lyd_node **first);
/**
* @brief Set dflt flag for a NP container if applicable, recursively for parents.
*
* @param[in] node Node whose criteria for the dflt flag has changed.
*/
void lyd_cont_set_dflt(struct lyd_node *node);
/**
* @brief Search in the given siblings (NOT recursively) for the first schema node data instance.
* Uses hashes - should be used whenever possible for best performance.
@ -147,6 +140,20 @@ LY_ERR lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lys
*/
void lyd_del_move_root(struct lyd_node **root, const struct lyd_node *to_del, const struct lys_module *mod);
/**
* @brief After adding a default child, check the node and all of its parent NP containers and set their dflt flag.
*
* @param[in] parent Changed first parent to check.
*/
void lyd_np_cont_dflt_set(struct lyd_node *parent);
/**
* @brief After adding a non-default child, remove the dflt flag from parent and other parent NP containers.
*
* @param[in] parent Changed first parent to update.
*/
void lyd_np_cont_dflt_del(struct lyd_node *parent);
/**
* @brief Try to get schema node for data with a parent based on an extension instance.
*
@ -345,7 +352,24 @@ LY_ERR lyd_create_opaq(const struct ly_ctx *ctx, const char *name, size_t name_l
LY_VALUE_FORMAT format, void *val_prefix_data, uint32_t hints, struct lyd_node **node);
/**
* @brief Check the existence and create any non-existing implicit siblings, recursively for the created nodes.
* @brief Change the value of a term (leaf or leaf-list) node.
*
* Node changed this way is always considered explicitly set, meaning its default flag
* is always cleared.
*
* @param[in] term Term node to change.
* @param[in] val New value to use.
* @param[in] use_val Whether @p val can be used and spent or should only be duplicated.
* @param[in] is_dflt Whether @p val is a default value or not.
* @return LY_SUCCESS if value was changed,
* @return LY_EEXIST if value was the same and only the default flag was cleared,
* @return LY_ENOT if the values were equal and no change occured,
* @return LY_ERR value on other errors.
*/
LY_ERR lyd_change_term_val(struct lyd_node *term, struct lyd_value *val, ly_bool use_val, ly_bool is_dflt);
/**
* @brief Check the existence and create any non-existing implicit children.
*
* @param[in] parent Parent of the potential default values, NULL for top-level siblings.
* @param[in,out] first First sibling.
@ -355,12 +379,32 @@ LY_ERR lyd_create_opaq(const struct ly_ctx *ctx, const char *name, size_t name_l
* @param[in] node_types Optional set to add nodes with unresolved types into.
* @param[in] ext_node Optional set to add nodes with extension instance node callbacks into.
* @param[in] impl_opts Implicit options (@ref implicitoptions).
* @param[in,out] getnext_ht Getnext HT to use, new @p sparent is added to it.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
LY_ERR lyd_new_implicit(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *ext_node,
uint32_t impl_opts, struct ly_ht *getnext_ht, struct lyd_node **diff);
/**
* @brief Check the existence and create any non-existing implicit children, recursively for containers.
*
* @param[in] parent Parent of the potential default values, NULL for top-level siblings.
* @param[in,out] first First sibling.
* @param[in] sparent Schema parent of the siblings, NULL if schema of @p parent can be used.
* @param[in] mod Module of the default values, NULL for nested siblings.
* @param[in] node_when Optional set to add nodes with "when" conditions into.
* @param[in] node_types Optional set to add nodes with unresolved types into.
* @param[in] ext_node Optional set to add nodes with extension instance node callbacks into.
* @param[in] impl_opts Implicit options (@ref implicitoptions).
* @param[in,out] getnext_ht Getnext HT to use, new @p sparent is added to it.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
LY_ERR lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *ext_node,
uint32_t impl_opts, struct lyd_node **diff);
uint32_t impl_opts, struct ly_ht *getnext_ht, struct lyd_node **diff);
/**
* @brief Find the next node, before which to insert the new node.
@ -719,4 +763,17 @@ LY_ERR lyd_unlink_leafref_node(const struct lyd_node_term *node, const struct ly
*/
void lyd_unlink_ignore_lyds(struct lyd_node **first_sibling, struct lyd_node *node);
/**
* @brief Check a string matches a compiled pattern.
*
* @param[in] pcode Compiled pattern.
* @param[in] str String to match.
* @param[in] str_len Length of @p str.
* @param[out] err Error, if generated.
* @return LY_SUCCESS on a match;
* @return LY_ENOT if the string does not match;
* @return LY_ERR on error.
*/
LY_ERR ly_pattern_code_match(pcre2_code *pcode, const char *str, size_t str_len, struct ly_err_item **err);
#endif /* LY_TREE_DATA_INTERNAL_H_ */

View file

@ -1243,14 +1243,15 @@ lyd_new_attr2(struct lyd_node *parent, const char *module_ns, const char *name,
*
* Reinserting ensures that the node is in the correct position and the data instances remain properly ordered.
*
* @param[in] term Term node to change. If it is a key, the parental list is inserted again.
* @param[in] term Term node to change. If it is a key, the parent list is reinserted.
* @param[in] val New value for @p term.
* @return LY_SUCCESS on success.
* @param[in] use_val Whether @p val can be used and spent or should only be duplicated.
* @return LY_ERR value.
*/
static LY_ERR
lyd_change_node_value(struct lyd_node_term *term, struct lyd_value *val)
lyd_change_node_value(struct lyd_node_term *term, struct lyd_value *val, ly_bool use_val)
{
LY_ERR ret = LY_SUCCESS;
LY_ERR rc = LY_SUCCESS;
struct lyd_node *target, *first;
if (term->schema->nodetype == LYS_LEAFLIST) {
@ -1260,33 +1261,115 @@ lyd_change_node_value(struct lyd_node_term *term, struct lyd_value *val)
} else {
/* just change the value */
term->value.realtype->plugin->free(LYD_CTX(term), &term->value);
term->value = *val;
if (use_val) {
term->value = *val;
} else {
rc = ((struct lysc_node_leaf *)term->schema)->type->plugin->duplicate(LYD_CTX(term), val, &term->value);
}
/* leaf that is not a key, its value is not used for its hash so it does not change */
return LY_SUCCESS;
return rc;
}
if (!LYD_NODE_IS_ALONE(target) && lyds_is_supported(target)) {
/* changing the value may cause a change in the order */
first = lyd_first_sibling(target);
first = first == target ? first->next : first;
/* unlink hash and unlink the target node in the lyds tree */
lyd_unlink_tree(target);
/* change value */
term->value.realtype->plugin->free(LYD_CTX(term), &term->value);
term->value = *val;
if (use_val) {
term->value = *val;
} else {
rc = ((struct lysc_node_leaf *)term->schema)->type->plugin->duplicate(LYD_CTX(term), val, &term->value);
}
/* reinserting */
lyd_insert_node(NULL, &first, target, LYD_INSERT_NODE_DEFAULT);
} else {
/* unlink hash */
lyd_unlink_hash(target);
/* change value */
term->value.realtype->plugin->free(LYD_CTX(term), &term->value);
term->value = *val;
if (use_val) {
term->value = *val;
} else {
rc = ((struct lysc_node_leaf *)term->schema)->type->plugin->duplicate(LYD_CTX(term), val, &term->value);
}
}
lyd_hash(target);
ret = lyd_insert_hash(target);
return ret;
lyd_hash(target);
rc = lyd_insert_hash(target);
return rc;
}
LY_ERR
lyd_change_term_val(struct lyd_node *term, struct lyd_value *val, ly_bool use_val, ly_bool is_dflt)
{
LY_ERR rc = LY_SUCCESS;
struct lysc_type *type;
struct lyd_node_term *t;
ly_bool dflt_change, val_change;
t = (struct lyd_node_term *)term;
type = ((struct lysc_node_leaf *)term->schema)->type;
/* compare original and new value */
if (type->plugin->compare(LYD_CTX(term), &t->value, val)) {
/* since they are different, they cannot both be default */
assert(!(term->flags & LYD_DEFAULT) || !is_dflt);
/* values differ, switch them */
LY_CHECK_RET(lyd_change_node_value(t, val, use_val));
val_change = 1;
} else {
/* same values, free the new stored one */
if (use_val) {
type->plugin->free(LYD_CTX(term), val);
}
val_change = 0;
}
/* clear links to leafref nodes */
if (val_change && (ly_ctx_get_options(LYD_CTX(term)) & LY_CTX_LEAFREF_LINKING)) {
lyd_free_leafref_nodes(t);
}
/* update flags */
if (val_change) {
term->flags |= LYD_NEW;
}
if ((term->flags & LYD_DEFAULT) && !is_dflt) {
/* remove dflt flag */
term->flags &= ~LYD_DEFAULT;
/* remove parent dflt flag */
lyd_np_cont_dflt_del(lyd_parent(term));
dflt_change = 1;
} else if (!(term->flags & LYD_DEFAULT) && is_dflt) {
/* add dflt flag */
term->flags |= LYD_DEFAULT;
/* add parent dflt flag */
lyd_np_cont_dflt_set(lyd_parent(term));
dflt_change = 1;
} else {
dflt_change = 0;
}
if (!val_change) {
/* only default flag change or no change */
rc = dflt_change ? LY_EEXIST : LY_ENOT;
} /* else value changed, LY_SUCCESS */
return rc;
}
/**
@ -1307,68 +1390,20 @@ lyd_change_node_value(struct lyd_node_term *term, struct lyd_value *val)
static LY_ERR
_lyd_change_term(struct lyd_node *term, const void *value, size_t value_len, LY_VALUE_FORMAT format)
{
LY_ERR ret = LY_SUCCESS;
struct lysc_type *type;
struct lyd_node_term *t;
struct lyd_node *parent;
LY_ERR r;
struct lyd_value val;
ly_bool dflt_change, val_change;
assert(term && term->schema && (term->schema->nodetype & LYD_NODE_TERM));
t = (struct lyd_node_term *)term;
type = ((struct lysc_node_leaf *)term->schema)->type;
/* parse the new value */
LOG_LOCSET(term->schema, term);
ret = lyd_value_store(LYD_CTX(term), &val, type, value, value_len, 0, 0, NULL, format, NULL, LYD_HINT_DATA,
term->schema, NULL);
r = lyd_value_store(LYD_CTX(term), &val, ((struct lysc_node_leaf *)term->schema)->type, value, value_len, 0, 0,
NULL, format, NULL, LYD_HINT_DATA, term->schema, NULL);
LOG_LOCBACK(1, 1);
LY_CHECK_GOTO(ret, cleanup);
LY_CHECK_RET(r);
/* compare original and new value */
if (type->plugin->compare(LYD_CTX(term), &t->value, &val)) {
/* values differ, switch them */
lyd_change_node_value(t, &val);
/* make the node non-validated */
term->flags &= LYD_NEW;
val_change = 1;
} else {
/* same values, free the new stored one */
type->plugin->free(LYD_CTX(term), &val);
val_change = 0;
}
/* clear links to leafref nodes */
if (ly_ctx_get_options(LYD_CTX(term)) & LY_CTX_LEAFREF_LINKING) {
lyd_free_leafref_nodes(t);
}
/* always clear the default flag */
if (term->flags & LYD_DEFAULT) {
for (parent = term; parent; parent = lyd_parent(parent)) {
parent->flags &= ~LYD_DEFAULT;
}
/* make the node non-validated */
term->flags &= LYD_NEW;
dflt_change = 1;
} else {
dflt_change = 0;
}
/* return value */
if (!val_change) {
if (dflt_change) {
/* only default flag change */
ret = LY_EEXIST;
} else {
/* no change */
ret = LY_ENOT;
}
} /* else value changed, LY_SUCCESS */
cleanup:
return ret;
/* change it */
return lyd_change_term_val(term, &val, 1, 0);
}
LIBYANG_API_DEF LY_ERR
@ -1623,13 +1658,14 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
LY_ERR ret = LY_SUCCESS, r;
struct lyxp_expr *exp = NULL;
struct ly_path *p = NULL;
struct lyd_node *nparent = NULL, *nnode = NULL, *node = NULL, *cur_parent;
struct lyd_node *nparent = NULL, *nnode = NULL, *node = NULL, *cur_parent, *iter;
const struct lysc_node *schema;
const struct lyd_value *val = NULL;
ly_bool store_only = (options & LYD_NEW_VAL_STORE_ONLY) ? 1 : 0;
ly_bool any_use_value = (options & LYD_NEW_ANY_USE_VALUE) ? 1 : 0;
LY_ARRAY_COUNT_TYPE path_idx = 0, orig_count = 0;
LY_VALUE_FORMAT format;
uint32_t hints;
uint32_t hints, count;
assert(parent || ctx);
assert(path && ((path[0] == '/') || parent));
@ -1676,7 +1712,7 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
goto cleanup;
} /* else we were not searching for the whole path */
} else if (r == LY_EINCOMPLETE) {
/* some nodes were found, adjust the iterator to the next segment */
/* some nodes were found, adjust the iterator to the next segment to be created */
++path_idx;
} else if (r == LY_ENOTFOUND) {
/* we will create the nodes from top-level, default behavior (absolute path), or from the parent (relative path) */
@ -1695,6 +1731,29 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
LY_ARRAY_INCREMENT(p);
}
if ((path_idx < LY_ARRAY_COUNT(p)) && lysc_is_dup_inst_list(p[path_idx].node) && p[path_idx].predicates &&
(p[path_idx].predicates[0].type == LY_PATH_PREDTYPE_POSITION)) {
/* check the used position of a key-less list or state leaf-list */
count = 0;
LYD_LIST_FOR_INST(node ? lyd_child(node) : parent, p[path_idx].node, iter) {
++count;
}
if (count + 1 < p[path_idx].predicates[0].position) {
if (count) {
LOGVAL(ctx, LYVE_REFERENCE,
"Cannot create \"%s\" on position %" PRIu64 ", only %" PRIu32 " instance%s exist%s.",
p[path_idx].node->name, p[path_idx].predicates[0].position, count, (count > 1) ? "s" : "",
(count > 1) ? "" : "s");
} else {
LOGVAL(ctx, LYVE_REFERENCE, "Cannot create \"%s\" on position %" PRIu64 ", no instances exist.",
p[path_idx].node->name, p[path_idx].predicates[0].position);
}
ret = LY_EINVAL;
goto cleanup;
}
}
/* create all the non-existing nodes in a loop */
for ( ; path_idx < LY_ARRAY_COUNT(p); ++path_idx) {
cur_parent = node;
@ -1789,7 +1848,7 @@ lyd_new_path_(struct lyd_node *parent, const struct ly_ctx *ctx, const struct ly
break;
case LYS_ANYDATA:
case LYS_ANYXML:
LY_CHECK_GOTO(ret = lyd_create_any(schema, value, value_type, 0, &node), cleanup);
LY_CHECK_GOTO(ret = lyd_create_any(schema, value, value_type, any_use_value, &node), cleanup);
break;
default:
LOGINT(ctx);
@ -1875,16 +1934,16 @@ lyd_new_ext_path(struct lyd_node *parent, const struct lysc_ext_instance *ext, c
}
LY_ERR
lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
lyd_new_implicit(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *ext_node,
uint32_t impl_opts, struct lyd_node **diff)
uint32_t impl_opts, struct ly_ht *getnext_ht, struct lyd_node **diff)
{
LY_ERR ret;
const struct lysc_node *iter = NULL;
const struct lysc_node *snode, **choices, **snodes;
struct lyd_node *node = NULL;
struct lyd_value **dflts;
LY_ARRAY_COUNT_TYPE u;
uint32_t getnext_opts;
uint32_t i;
assert(first && (parent || sparent || mod));
@ -1892,40 +1951,51 @@ lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struc
sparent = parent->schema;
}
getnext_opts = LYS_GETNEXT_WITHCHOICE;
if (impl_opts & LYD_IMPLICIT_OUTPUT) {
getnext_opts |= LYS_GETNEXT_OUTPUT;
}
/* get cached getnext schema nodes */
LY_CHECK_RET(lyd_val_getnext_get(sparent, mod, NULL, impl_opts & LYD_IMPLICIT_OUTPUT, getnext_ht, &choices, &snodes));
while ((iter = lys_getnext(iter, sparent, mod ? mod->compiled : NULL, getnext_opts))) {
if ((impl_opts & LYD_IMPLICIT_NO_STATE) && (iter->flags & LYS_CONFIG_R)) {
/* choice nodes */
for (i = 0; choices && choices[i]; ++i) {
snode = choices[i];
if ((impl_opts & LYD_IMPLICIT_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
continue;
} else if ((impl_opts & LYD_IMPLICIT_NO_CONFIG) && (iter->flags & LYS_CONFIG_W)) {
} else if ((impl_opts & LYD_IMPLICIT_NO_CONFIG) && (snode->flags & LYS_CONFIG_W)) {
continue;
}
switch (iter->nodetype) {
case LYS_CHOICE:
node = lys_getnext_data(NULL, *first, NULL, iter, NULL);
if (!node && ((struct lysc_node_choice *)iter)->dflt) {
/* create default case data */
LY_CHECK_RET(lyd_new_implicit_r(parent, first, &((struct lysc_node_choice *)iter)->dflt->node,
NULL, node_when, node_types, ext_node, impl_opts, diff));
} else if (node) {
/* create any default data in the existing case */
assert(node->schema->parent->nodetype == LYS_CASE);
LY_CHECK_RET(lyd_new_implicit_r(parent, first, node->schema->parent, NULL, node_when, node_types,
ext_node, impl_opts, diff));
}
break;
node = lys_getnext_data(NULL, *first, NULL, snode, NULL);
if (!node && ((struct lysc_node_choice *)snode)->dflt) {
/* create default case data */
LY_CHECK_RET(lyd_new_implicit(parent, first, &((struct lysc_node_choice *)snode)->dflt->node,
NULL, node_when, node_types, ext_node, impl_opts, getnext_ht, diff));
} else if (node) {
/* create any default data in the existing case */
assert(node->schema->parent->nodetype == LYS_CASE);
LY_CHECK_RET(lyd_new_implicit(parent, first, node->schema->parent, NULL, node_when, node_types, ext_node,
impl_opts, getnext_ht, diff));
}
}
/* container, leaf, leaf-list nodes */
for (i = 0; snodes && snodes[i]; ++i) {
snode = snodes[i];
if ((impl_opts & LYD_IMPLICIT_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
continue;
} else if ((impl_opts & LYD_IMPLICIT_NO_CONFIG) && (snode->flags & LYS_CONFIG_W)) {
continue;
}
switch (snode->nodetype) {
case LYS_CONTAINER:
if (!(iter->flags & LYS_PRESENCE) && lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
if (!(snode->flags & LYS_PRESENCE) && lyd_find_sibling_val(*first, snode, NULL, 0, NULL)) {
/* create default NP container */
LY_CHECK_RET(lyd_create_inner(iter, &node));
node->flags = LYD_DEFAULT | (lysc_has_when(iter) ? LYD_WHEN_TRUE : 0);
LY_CHECK_RET(lyd_create_inner(snode, &node));
node->flags = LYD_DEFAULT | (lysc_has_when(snode) ? LYD_WHEN_TRUE : 0);
lyd_insert_node(parent, first, node, LYD_INSERT_NODE_DEFAULT);
if (lysc_has_when(iter) && node_when) {
if (lysc_has_when(snode) && node_when) {
/* remember to resolve when */
LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
}
@ -1937,17 +2007,13 @@ lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struc
/* add into diff */
LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_CREATE, diff));
}
/* create any default children */
LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, node_when, node_types,
ext_node, impl_opts, diff));
}
break;
case LYS_LEAF:
if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaf *)iter)->dflt &&
lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaf *)snode)->dflt &&
lyd_find_sibling_val(*first, snode, NULL, 0, NULL)) {
/* create default leaf */
ret = lyd_create_term2(iter, ((struct lysc_node_leaf *)iter)->dflt, &node);
ret = lyd_create_term2(snode, ((struct lysc_node_leaf *)snode)->dflt, &node);
if (ret == LY_EINCOMPLETE) {
if (node_types) {
/* remember to resolve type */
@ -1956,10 +2022,10 @@ lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struc
} else if (ret) {
return ret;
}
node->flags = LYD_DEFAULT | (lysc_has_when(iter) ? LYD_WHEN_TRUE : 0);
node->flags = LYD_DEFAULT | (lysc_has_when(snode) ? LYD_WHEN_TRUE : 0);
lyd_insert_node(parent, first, node, LYD_INSERT_NODE_DEFAULT);
if (lysc_has_when(iter) && node_when) {
if (lysc_has_when(snode) && node_when) {
/* remember to resolve when */
LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
}
@ -1974,12 +2040,12 @@ lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struc
}
break;
case LYS_LEAFLIST:
if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaflist *)iter)->dflts &&
lyd_find_sibling_val(*first, iter, NULL, 0, NULL)) {
if (!(impl_opts & LYD_IMPLICIT_NO_DEFAULTS) && ((struct lysc_node_leaflist *)snode)->dflts &&
lyd_find_sibling_val(*first, snode, NULL, 0, NULL)) {
/* create all default leaf-lists */
dflts = ((struct lysc_node_leaflist *)iter)->dflts;
dflts = ((struct lysc_node_leaflist *)snode)->dflts;
LY_ARRAY_FOR(dflts, u) {
ret = lyd_create_term2(iter, dflts[u], &node);
ret = lyd_create_term2(snode, dflts[u], &node);
if (ret == LY_EINCOMPLETE) {
if (node_types) {
/* remember to resolve type */
@ -1988,10 +2054,10 @@ lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struc
} else if (ret) {
return ret;
}
node->flags = LYD_DEFAULT | (lysc_has_when(iter) ? LYD_WHEN_TRUE : 0);
node->flags = LYD_DEFAULT | (lysc_has_when(snode) ? LYD_WHEN_TRUE : 0);
lyd_insert_node(parent, first, node, LYD_INSERT_NODE_DEFAULT);
if (lysc_has_when(iter) && node_when) {
if (lysc_has_when(snode) && node_when) {
/* remember to resolve when */
LY_CHECK_RET(ly_set_add(node_when, node, 1, NULL));
}
@ -2015,38 +2081,64 @@ lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struc
return LY_SUCCESS;
}
LY_ERR
lyd_new_implicit_r(struct lyd_node *parent, struct lyd_node **first, const struct lysc_node *sparent,
const struct lys_module *mod, struct ly_set *node_when, struct ly_set *node_types, struct ly_set *ext_node,
uint32_t impl_opts, struct ly_ht *getnext_ht, struct lyd_node **diff)
{
struct lyd_node *child;
/* parent children */
LY_CHECK_RET(lyd_new_implicit(parent, first, sparent, mod, node_when, node_types, ext_node, impl_opts, getnext_ht, diff));
LY_LIST_FOR(parent ? lyd_child_no_keys(parent) : *first, child) {
/* recursively for all the containers */
if ((child->flags & LYD_DEFAULT) && (child->schema->nodetype == LYS_CONTAINER)) {
LY_CHECK_RET(lyd_new_implicit_r(child, lyd_node_child_p(child), NULL, mod, node_when, node_types, ext_node,
impl_opts, getnext_ht, diff));
}
}
return LY_SUCCESS;
}
LIBYANG_API_DEF LY_ERR
lyd_new_implicit_tree(struct lyd_node *tree, uint32_t implicit_options, struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
LY_ERR rc = LY_SUCCESS;
struct lyd_node *node;
struct ly_set node_when = {0};
struct ly_ht *getnext_ht = NULL;
LY_CHECK_ARG_RET(NULL, tree, LY_EINVAL);
if (diff) {
*diff = NULL;
}
/* create the getnext hash table */
LY_CHECK_GOTO(rc = lyd_val_getnext_ht_new(&getnext_ht), cleanup);
LYD_TREE_DFS_BEGIN(tree, node) {
if (node->schema && (node->schema->nodetype & LYD_NODE_INNER)) {
LY_CHECK_GOTO(ret = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, &node_when, NULL,
NULL, implicit_options, diff), cleanup);
LY_CHECK_GOTO(rc = lyd_new_implicit(node, lyd_node_child_p(node), NULL, NULL, &node_when, NULL,
NULL, implicit_options, getnext_ht, diff), cleanup);
}
LYD_TREE_DFS_END(tree, node);
}
/* resolve when and remove any invalid defaults */
ret = lyd_validate_unres(&tree, NULL, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, NULL, 0, diff);
LY_CHECK_GOTO(ret, cleanup);
rc = lyd_validate_unres(&tree, NULL, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, NULL, 0, diff);
LY_CHECK_GOTO(rc, cleanup);
cleanup:
ly_set_erase(&node_when, NULL);
if (ret && diff) {
lyd_val_getnext_ht_free(getnext_ht);
if (rc && diff) {
lyd_free_all(*diff);
*diff = NULL;
}
return ret;
return rc;
}
LIBYANG_API_DEF LY_ERR
@ -2055,7 +2147,7 @@ lyd_new_implicit_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t
const struct lys_module *mod;
struct lyd_node *d = NULL;
uint32_t i = 0;
LY_ERR ret = LY_SUCCESS;
LY_ERR rc = LY_SUCCESS;
LY_CHECK_ARG_RET(ctx, tree, *tree || ctx, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, ctx, LY_EINVAL);
@ -2072,7 +2164,7 @@ lyd_new_implicit_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t
continue;
}
LY_CHECK_GOTO(ret = lyd_new_implicit_module(tree, mod, implicit_options, diff ? &d : NULL), cleanup);
LY_CHECK_GOTO(rc = lyd_new_implicit_module(tree, mod, implicit_options, diff ? &d : NULL), cleanup);
if (d) {
/* merge into one diff */
lyd_insert_sibling(*diff, d, diff);
@ -2082,20 +2174,21 @@ lyd_new_implicit_all(struct lyd_node **tree, const struct ly_ctx *ctx, uint32_t
}
cleanup:
if (ret && diff) {
if (rc && diff) {
lyd_free_all(*diff);
*diff = NULL;
}
return ret;
return rc;
}
LIBYANG_API_DEF LY_ERR
lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module, uint32_t implicit_options,
struct lyd_node **diff)
{
LY_ERR ret = LY_SUCCESS;
LY_ERR rc = LY_SUCCESS;
struct lyd_node *root, *d = NULL;
struct ly_set node_when = {0};
struct ly_ht *getnext_ht = NULL;
LY_CHECK_ARG_RET(NULL, tree, module, LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(*tree ? LYD_CTX(*tree) : NULL, module ? module->ctx : NULL, LY_EINVAL);
@ -2103,17 +2196,20 @@ lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module,
*diff = NULL;
}
/* create the getnext hash table for this module */
LY_CHECK_GOTO(rc = lyd_val_getnext_ht_new(&getnext_ht), cleanup);
/* add all top-level defaults for this module */
LY_CHECK_GOTO(ret = lyd_new_implicit_r(NULL, tree, NULL, module, &node_when, NULL, NULL, implicit_options, diff),
cleanup);
rc = lyd_new_implicit(NULL, tree, NULL, module, &node_when, NULL, NULL, implicit_options, getnext_ht, diff);
LY_CHECK_GOTO(rc, cleanup);
/* resolve when and remove any invalid defaults */
LY_CHECK_GOTO(ret = lyd_validate_unres(tree, module, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, NULL,
LY_CHECK_GOTO(rc = lyd_validate_unres(tree, module, 0, &node_when, LYXP_IGNORE_WHEN, NULL, NULL, NULL, NULL,
0, diff), cleanup);
/* process nested nodes */
/* process top-level (and nested) nodes */
LY_LIST_FOR(*tree, root) {
LY_CHECK_GOTO(ret = lyd_new_implicit_tree(root, implicit_options, diff ? &d : NULL), cleanup);
LY_CHECK_GOTO(rc = lyd_new_implicit_tree(root, implicit_options, diff ? &d : NULL), cleanup);
if (d) {
/* merge into one diff */
@ -2124,9 +2220,10 @@ lyd_new_implicit_module(struct lyd_node **tree, const struct lys_module *module,
cleanup:
ly_set_erase(&node_when, NULL);
if (ret && diff) {
lyd_val_getnext_ht_free(getnext_ht);
if (rc && diff) {
lyd_free_all(*diff);
*diff = NULL;
}
return ret;
return rc;
}

View file

@ -935,7 +935,7 @@ lyds_pool_clean(struct lyds_pool *pool)
pool->rbn = NULL;
for (meta = pool->meta; meta; meta = next) {
next = meta->next ? meta->next : NULL;
next = meta->next;
RBT_SET(meta, NULL);
lyd_free_meta_single(meta);
}
@ -1055,9 +1055,10 @@ lyds_additionally_create_rb_nodes(struct lyd_node **first_sibling, struct lyd_no
LY_ERR ret;
ly_bool max;
struct rb_node *rbn;
struct lyd_node *iter;
struct lyd_node *iter, *next;
for (iter = node; iter && (iter->schema == (*leader)->schema); iter = iter->next) {
for (iter = node; iter && (iter->schema == (*leader)->schema); iter = next) {
next = iter->next;
ret = lyds_create_node(iter, &rbn);
LY_CHECK_RET(ret);
rb_insert_node(rbt, rbn, &max);
@ -1118,7 +1119,7 @@ lyds_additionally_reuse_rb_tree(struct lyd_node **first_sibling, struct lyd_node
struct rb_node **rbt, struct lyds_pool *pool, struct lyd_node **next)
{
ly_bool max;
struct lyd_node *iter;
struct lyd_node *iter, *next_node;
/* let's begin with the leader */
RBN_RESET(pool->rbn, *leader);
@ -1126,7 +1127,8 @@ lyds_additionally_reuse_rb_tree(struct lyd_node **first_sibling, struct lyd_node
pool->rbn = rb_iter_next(&pool->iter_state);
/* continue with the rest of the nodes */
for (iter = (*leader)->next; iter && (iter->schema == (*leader)->schema); iter = iter->next) {
for (iter = (*leader)->next; iter && (iter->schema == (*leader)->schema); iter = next_node) {
next_node = iter->next;
if (!pool->rbn) {
*next = iter;
return;

View file

@ -91,8 +91,8 @@ lysp_check_date(struct lysp_ctx *ctx, const char *date, size_t date_len, const c
}
memcpy(&tm_, &tm, sizeof tm);
/* DST may move the hour back resulting in a different day */
tm_.tm_hour = 1;
/* Disabling DST: Set tm_isdst to -1 so that mktime() won't adjust for daylight saving time. */
tm_.tm_isdst = -1;
mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */
if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */

View file

@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Validation
*
* Copyright (c) 2019 - 2023 CESNET, z.s.p.o.
* Copyright (c) 2019 - 2024 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.
@ -56,6 +56,109 @@
} \
}
/**
* @brief Callback for freeing getnext HT values.
*/
static void
lyd_val_getnext_ht_free_cb(void *val_p)
{
struct lyd_val_getnext *val = val_p;
free(val->snodes);
free(val->choices);
}
/**
* @brief Callback for checking getnext HT value equality.
*/
static ly_bool
lyd_val_getnext_ht_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
{
struct lyd_val_getnext *val1 = val1_p;
struct lyd_val_getnext *val2 = val2_p;
if (val1->sparent == val2->sparent) {
return 1;
}
return 0;
}
LY_ERR
lyd_val_getnext_ht_new(struct ly_ht **getnext_ht_p)
{
*getnext_ht_p = lyht_new(32, sizeof(struct lyd_val_getnext), lyd_val_getnext_ht_equal_cb, NULL, 1);
if (!*getnext_ht_p) {
LOGMEM(NULL);
return LY_EMEM;
}
return LY_SUCCESS;
}
void
lyd_val_getnext_ht_free(struct ly_ht *getnext_ht)
{
lyht_free(getnext_ht, lyd_val_getnext_ht_free_cb);
}
LY_ERR
lyd_val_getnext_get(const struct lysc_node *sparent, const struct lys_module *mod, const struct lysc_ext_instance *ext,
ly_bool output, struct ly_ht *getnext_ht, const struct lysc_node ***choices, const struct lysc_node ***snodes)
{
LY_ERR rc = LY_SUCCESS;
struct lyd_val_getnext val = {0}, *getnext = NULL;
const struct lysc_node *snode = NULL;
uint32_t getnext_opts, snode_count = 0, choice_count = 0;
/* try to find the entry for this schema parent */
val.sparent = sparent;
if (!lyht_find(getnext_ht, &val, (uintptr_t)sparent, (void **)&getnext)) {
goto cleanup;
}
/* traverse all the children using getnext and store them */
getnext_opts = LYS_GETNEXT_WITHCHOICE | (output ? LYS_GETNEXT_OUTPUT : 0);
while (ext ? (snode = lys_getnext_ext(snode, sparent, ext, getnext_opts)) :
(snode = lys_getnext(snode, sparent, mod ? mod->compiled : NULL, getnext_opts))) {
if (snode->nodetype == LYS_CHOICE) {
/* store a choice node */
val.choices = ly_realloc(val.choices, (choice_count + 2) * sizeof *val.choices);
LY_CHECK_ERR_GOTO(!val.choices, LOGMEM(NULL); rc = LY_EMEM, cleanup);
val.choices[choice_count] = snode;
++choice_count;
} else {
/* store other nodes */
val.snodes = ly_realloc(val.snodes, (snode_count + 2) * sizeof *val.snodes);
LY_CHECK_ERR_GOTO(!val.snodes, LOGMEM(NULL); rc = LY_EMEM, cleanup);
val.snodes[snode_count] = snode;
++snode_count;
}
}
/* add terminating NULL items */
if (choice_count) {
val.choices[choice_count] = NULL;
}
if (snode_count) {
val.snodes[snode_count] = NULL;
}
/* add into the hash table */
if ((rc = lyht_insert(getnext_ht, &val, (uintptr_t)sparent, (void **)&getnext))) {
goto cleanup;
}
cleanup:
if (rc) {
free(val.snodes);
free(val.choices);
} else {
*choices = getnext->choices;
*snodes = getnext->snodes;
}
return rc;
}
LY_ERR
lyd_val_diff_add(const struct lyd_node *node, enum lyd_diff_op op, struct lyd_node **diff)
{
@ -434,6 +537,18 @@ cleanup:
return rc;
}
/**
* @brief Compare callback for finding duplicates in hash table.
*
* Implementation of ::lyht_value_equal_cb.
*/
static ly_bool
lyd_val_dup_val_equal(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *cb_data)
{
/* always find the same instance, not the exact same pointer (set mod = 0) */
return lyd_hash_table_val_equal(val1_p, val2_p, 0, cb_data);
}
/**
* @brief Validate instance duplication.
*
@ -445,7 +560,6 @@ cleanup:
static LY_ERR
lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *node, uint32_t val_opts)
{
struct lyd_node **match_p, *match;
ly_bool fail = 0;
assert(node->flags & LYD_NEW);
@ -458,12 +572,9 @@ lyd_validate_duplicates(const struct lyd_node *first, const struct lyd_node *nod
/* find exactly the same next instance using hashes if possible */
if (node->parent && node->parent->children_ht) {
lyd_find_sibling_first(first, node, &match);
assert(match);
if (match != node) {
fail = 1;
} else if (!lyht_find_next(node->parent->children_ht, &node, node->hash, (void **)&match_p)) {
/* because of the callback used, an instance must always be found (pointer may or may not be equal to node),
* so if we find another instance, there is a duplicate */
if (!lyht_find_next_with_collision_cb(node->parent->children_ht, &node, node->hash, lyd_val_dup_val_equal, NULL)) {
fail = 1;
}
} else {
@ -783,27 +894,37 @@ lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node **node,
* @param[in,out] first First sibling.
* @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
* @param[in] mod Module of the siblings, NULL for nested siblings.
* @param[in] ext Extension instance to use, if relevant.
* @param[in] val_opts Validation options.
* @param[in] int_opts Internal parser options.
* @param[in,out] getnext_ht Getnext HT to use, new @p sparent is added to it.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_choice_r(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
uint32_t val_opts, struct lyd_node **diff)
const struct lysc_ext_instance *ext, uint32_t val_opts, uint32_t int_opts, struct ly_ht *getnext_ht,
struct lyd_node **diff)
{
LY_ERR r, rc = LY_SUCCESS;
const struct lysc_node *snode = NULL;
const struct lysc_node **choices, **snodes;
uint32_t i;
while (*first && (snode = lys_getnext(snode, sparent, mod ? mod->compiled : NULL, LYS_GETNEXT_WITHCHOICE))) {
/* get cached getnext schema nodes */
rc = lyd_val_getnext_get(sparent, mod, ext, int_opts & LYD_INTOPT_REPLY, getnext_ht, &choices, &snodes);
LY_CHECK_GOTO(rc, cleanup);
if (!choices) {
goto cleanup;
}
for (i = 0; *first && choices[i]; ++i) {
/* check case duplicites */
if (snode->nodetype == LYS_CHOICE) {
r = lyd_validate_cases(first, mod, (struct lysc_node_choice *)snode, diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
r = lyd_validate_cases(first, mod, (struct lysc_node_choice *)choices[i], diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* check for nested choice */
r = lyd_validate_choice_r(first, snode, mod, val_opts, diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
/* check for nested choice */
r = lyd_validate_choice_r(first, choices[i], mod, ext, val_opts, int_opts, getnext_ht, diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
cleanup:
@ -812,7 +933,8 @@ cleanup:
LY_ERR
lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
uint32_t val_opts, struct lyd_node **diff)
const struct lysc_ext_instance *ext, uint32_t val_opts, uint32_t int_opts, struct ly_ht *getnext_ht,
struct lyd_node **diff)
{
LY_ERR r, rc = LY_SUCCESS;
struct lyd_node *node;
@ -821,7 +943,7 @@ lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const
assert(first && (sparent || mod));
/* validate choices */
r = lyd_validate_choice_r(first, sparent, mod, val_opts, diff);
r = lyd_validate_choice_r(first, sparent, mod, ext, val_opts, int_opts, getnext_ht, diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
node = *first;
@ -1369,38 +1491,76 @@ cleanup:
* @param[in] parent Data parent.
* @param[in] sparent Schema parent of the nodes to check.
* @param[in] mod Module of the nodes to check.
* @param[in] ext Extension instance to use, if relevant.
* @param[in] val_opts Validation options, see @ref datavalidationoptions.
* @param[in] int_opts Internal parser options.
* @param[in,out] getnext_ht Getnext HT to use, new @p sparent is added to it.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_node *parent,
const struct lysc_node *sparent, const struct lysc_module *mod, uint32_t val_opts, uint32_t int_opts)
const struct lysc_node *sparent, const struct lys_module *mod, const struct lysc_ext_instance *ext,
uint32_t val_opts, uint32_t int_opts, struct ly_ht *getnext_ht)
{
LY_ERR r, rc = LY_SUCCESS;
const struct lysc_node *snode = NULL, *scase;
const struct lysc_node *snode, *scase, **choices, **snodes;
struct lysc_node_list *slist;
struct lysc_node_leaflist *sllist;
uint32_t getnext_opts;
uint32_t i;
getnext_opts = LYS_GETNEXT_WITHCHOICE | (int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
/* get cached getnext schema nodes */
rc = lyd_val_getnext_get(sparent, mod, ext, int_opts & LYD_INTOPT_REPLY, getnext_ht, &choices, &snodes);
LY_CHECK_GOTO(rc, cleanup);
for (i = 0; choices && choices[i]; ++i) {
snode = choices[i];
/* disabled nodes are skipped by lys_getnext */
while ((snode = lys_getnext(snode, sparent, mod, getnext_opts))) {
if ((val_opts & LYD_VALIDATE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
/* skip state nodes */
continue;
}
if (snode->flags & LYS_MAND_TRUE) {
/* check generic mandatory existence */
r = lyd_validate_mandatory(first, parent, snode, val_opts);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
/* find the existing case, if any */
LY_LIST_FOR(lysc_node_child(snode), scase) {
if (lys_getnext_data(NULL, first, NULL, scase, NULL)) {
/* validate only this case */
r = lyd_validate_siblings_schema_r(first, parent, scase, mod, ext, val_opts, int_opts, getnext_ht);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
break;
}
}
}
for (i = 0; snodes && snodes[i]; ++i) {
snode = snodes[i];
if ((val_opts & LYD_VALIDATE_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
/* skip state nodes */
continue;
}
/* check min-elements and max-elements */
if (snode->nodetype == LYS_LIST) {
slist = (struct lysc_node_list *)snode;
if (slist->min || slist->max) {
if (slist->min || (slist->max < UINT32_MAX)) {
r = lyd_validate_minmax(first, parent, snode, slist->min, slist->max, val_opts);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
/* check unique */
if (slist->uniques) {
r = lyd_validate_unique(first, snode, (const struct lysc_node_leaf ***)slist->uniques, val_opts);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
} else if (snode->nodetype == LYS_LEAFLIST) {
sllist = (struct lysc_node_leaflist *)snode;
if (sllist->min || sllist->max) {
if (sllist->min || (sllist->max < UINT32_MAX)) {
r = lyd_validate_minmax(first, parent, snode, sllist->min, sllist->max, val_opts);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
@ -1410,27 +1570,6 @@ lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_no
r = lyd_validate_mandatory(first, parent, snode, val_opts);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
/* check unique */
if (snode->nodetype == LYS_LIST) {
slist = (struct lysc_node_list *)snode;
if (slist->uniques) {
r = lyd_validate_unique(first, snode, (const struct lysc_node_leaf ***)slist->uniques, val_opts);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
}
if (snode->nodetype == LYS_CHOICE) {
/* find the existing case, if any */
LY_LIST_FOR(lysc_node_child(snode), scase) {
if (lys_getnext_data(NULL, first, NULL, scase, NULL)) {
/* validate only this case */
r = lyd_validate_siblings_schema_r(first, parent, scase, mod, val_opts, int_opts);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
break;
}
}
}
}
cleanup:
@ -1557,14 +1696,17 @@ cleanup:
* @param[in] parent Data parent.
* @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
* @param[in] mod Module of the siblings, NULL for nested siblings.
* @param[in] ext Extension instance to use, if relevant.
* @param[in] val_opts Validation options (@ref datavalidationoptions).
* @param[in] int_opts Internal parser options.
* @param[in] must_xp_opts Additional XPath options to use for evaluating "must".
* @param[in,out] getnext_ht Getnext HT to use.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *sparent,
const struct lys_module *mod, uint32_t val_opts, uint32_t int_opts, uint32_t must_xp_opts)
const struct lys_module *mod, const struct lysc_ext_instance *ext, uint32_t val_opts, uint32_t int_opts,
uint32_t must_xp_opts, struct ly_ht *getnext_ht)
{
LY_ERR r, rc = LY_SUCCESS;
const char *innode;
@ -1626,7 +1768,7 @@ next_iter:
}
/* validate schema-based restrictions */
r = lyd_validate_siblings_schema_r(first, parent, sparent, mod ? mod->compiled : NULL, val_opts, int_opts);
r = lyd_validate_siblings_schema_r(first, parent, sparent, mod, ext, val_opts, int_opts, getnext_ht);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
LY_LIST_FOR(first, node) {
@ -1636,11 +1778,12 @@ next_iter:
}
/* validate all children recursively */
r = lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, val_opts, int_opts, must_xp_opts);
r = lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, NULL, val_opts, int_opts, must_xp_opts,
getnext_ht);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* set default for containers */
lyd_cont_set_dflt(node);
lyd_np_cont_dflt_set(node);
}
cleanup:
@ -1731,13 +1874,15 @@ lyd_validate_node_ext(struct lyd_node *node, struct ly_set *ext_node)
* @param[in,out] ext_node Set with nodes with extensions to validate.
* @param[in,out] ext_val Set for parsed extension data to validate.
* @param[in] val_opts Validation options.
* @param[in] int_opts Internal parser options.
* @param[in,out] getnext_ht Getnext HT to use.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
static LY_ERR
lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_set *node_types,
struct ly_set *meta_types, struct ly_set *ext_node, struct ly_set *ext_val, uint32_t val_opts,
struct lyd_node **diff)
uint32_t int_opts, struct ly_ht *getnext_ht, struct lyd_node **diff)
{
LY_ERR r, rc = LY_SUCCESS;
const struct lyd_meta *meta;
@ -1771,7 +1916,7 @@ lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
} else if (node->schema->nodetype & LYD_NODE_INNER) {
/* new node validation, autodelete */
r = lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, val_opts, diff);
r = lyd_validate_new(lyd_node_child_p(node), node->schema, NULL, NULL, val_opts, int_opts, getnext_ht, diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* add nested defaults */
@ -1782,7 +1927,7 @@ lyd_validate_subtree(struct lyd_node *root, struct ly_set *node_when, struct ly_
if (val_opts & LYD_VALIDATE_NO_DEFAULTS) {
impl_opts |= LYD_IMPLICIT_NO_DEFAULTS;
}
r = lyd_new_implicit_r(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, NULL, impl_opts, diff);
r = lyd_new_implicit(node, lyd_node_child_p(node), NULL, NULL, NULL, NULL, NULL, impl_opts, getnext_ht, diff);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
@ -1814,6 +1959,7 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
const struct lys_module *mod;
struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_node = {0}, ext_val = {0};
uint32_t i = 0, impl_opts;
struct ly_ht *getnext_ht = NULL;
assert(tree && ctx);
assert((node_when_p && node_types_p && meta_types_p && ext_node_p && ext_val_p) ||
@ -1844,8 +1990,13 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
first2 = &first;
}
/* create the getnext hash table for this module */
r = lyd_val_getnext_ht_new(&getnext_ht);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
/* validate new top-level nodes of this module, autodelete */
r = lyd_validate_new(first2, *first2 ? lysc_data_parent((*first2)->schema) : NULL, mod, val_opts, diff);
r = lyd_validate_new(first2, *first2 ? lysc_data_parent((*first2)->schema) : NULL, mod, NULL, val_opts, 0,
getnext_ht, diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
/* add all top-level defaults for this module, if going to validate subtree, do not add into unres sets
@ -1857,9 +2008,15 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
if (val_opts & LYD_VALIDATE_NO_DEFAULTS) {
impl_opts |= LYD_IMPLICIT_NO_DEFAULTS;
}
r = lyd_new_implicit_r(lyd_parent(*first2), first2, NULL, mod, validate_subtree ? NULL : node_when_p,
validate_subtree ? NULL : node_types_p, validate_subtree ? NULL : ext_node_p, impl_opts, diff);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (validate_subtree) {
r = lyd_new_implicit(lyd_parent(*first2), first2, NULL, mod, NULL, NULL, NULL, impl_opts, getnext_ht, diff);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
} else {
/* descendants will not be validated, create them all */
r = lyd_new_implicit_r(lyd_parent(*first2), first2, NULL, mod, node_when_p, node_types_p, ext_node_p,
impl_opts, getnext_ht, diff);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
}
/* our first module node pointer may no longer be the first */
first = *first2;
@ -1878,7 +2035,7 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
}
r = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p,
val_opts, diff);
val_opts, 0, getnext_ht, diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
}
@ -1890,9 +2047,13 @@ lyd_validate(struct lyd_node **tree, const struct lys_module *module, const stru
if (!(val_opts & LYD_VALIDATE_NOT_FINAL)) {
/* perform final validation that assumes the data tree is final */
r = lyd_validate_final_r(*first2, NULL, NULL, mod, val_opts, 0, 0);
r = lyd_validate_final_r(*first2, NULL, NULL, mod, NULL, val_opts, 0, 0, getnext_ht);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
/* free the getnext hash table */
lyht_free(getnext_ht, lyd_val_getnext_ht_free_cb);
getnext_ht = NULL;
}
cleanup:
@ -1901,6 +2062,63 @@ cleanup:
ly_set_erase(&meta_types, NULL);
ly_set_erase(&ext_node, free);
ly_set_erase(&ext_val, free);
lyd_val_getnext_ht_free(getnext_ht);
return rc;
}
LY_ERR
lyd_validate_ext(struct lyd_node **tree, const struct lysc_ext_instance *ext, uint32_t val_opts,
ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff)
{
LY_ERR r, rc = LY_SUCCESS;
struct lyd_node *iter;
struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_node = {0}, ext_val = {0};
struct ly_ht *getnext_ht = NULL;
assert(tree);
assert((node_when_p && node_types_p && meta_types_p && ext_node_p && ext_val_p) ||
(!node_when_p && !node_types_p && !meta_types_p && !ext_node_p && !ext_val_p));
if (!node_when_p) {
node_when_p = &node_when;
node_types_p = &node_types;
meta_types_p = &meta_types;
ext_node_p = &ext_node;
ext_val_p = &ext_val;
}
/* create the getnext hash table for these data */
r = lyd_val_getnext_ht_new(&getnext_ht);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
if (validate_subtree) {
/* process nested nodes */
LY_LIST_FOR(*tree, iter) {
r = lyd_validate_subtree(iter, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p,
val_opts, 0, getnext_ht, diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
}
/* finish incompletely validated terminal values/attributes and when conditions */
r = lyd_validate_unres(tree, NULL, LYD_TYPE_DATA_YANG, node_when_p, 0, node_types_p, meta_types_p,
ext_node_p, ext_val_p, val_opts, diff);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
if (!(val_opts & LYD_VALIDATE_NOT_FINAL)) {
/* perform final validation that assumes the data tree is final */
r = lyd_validate_final_r(*tree, NULL, NULL, NULL, ext, val_opts, 0, 0, getnext_ht);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
}
cleanup:
ly_set_erase(&node_when, NULL);
ly_set_erase(&node_types, NULL);
ly_set_erase(&meta_types, NULL);
ly_set_erase(&ext_node, free);
ly_set_erase(&ext_val, free);
lyd_val_getnext_ht_free(getnext_ht);
return rc;
}
@ -1938,6 +2156,7 @@ lyd_validate_module_final(struct lyd_node *tree, const struct lys_module *module
struct lyd_node *first;
const struct lys_module *mod;
uint32_t i = 0;
struct ly_ht *getnext_ht = NULL;
LY_CHECK_ARG_RET(NULL, module, !(val_opts & (LYD_VALIDATE_PRESENT | LYD_VALIDATE_NOT_FINAL)), LY_EINVAL);
LY_CHECK_CTX_EQUAL_RET(tree ? LYD_CTX(tree) : NULL, module->ctx, LY_EINVAL);
@ -1946,11 +2165,16 @@ lyd_validate_module_final(struct lyd_node *tree, const struct lys_module *module
mod = lyd_mod_next_module(tree, module, module->ctx, &i, &first);
assert(mod);
/* create the getnext hash table for this module */
r = lyd_val_getnext_ht_new(&getnext_ht);
LY_CHECK_ERR_GOTO(r, rc = r, cleanup);
/* perform final validation that assumes the data tree is final */
r = lyd_validate_final_r(first, NULL, NULL, mod, val_opts, 0, 0);
r = lyd_validate_final_r(first, NULL, NULL, mod, NULL, val_opts, 0, 0, getnext_ht);
LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
cleanup:
lyd_val_getnext_ht_free(getnext_ht);
return rc;
}
@ -2043,6 +2267,7 @@ _lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struc
LY_ERR rc = LY_SUCCESS;
struct lyd_node *tree_sibling, *tree_parent, *op_subtree, *op_parent, *op_sibling_before, *op_sibling_after, *child;
struct ly_set node_types = {0}, meta_types = {0}, node_when = {0}, ext_node = {0}, ext_val = {0};
struct ly_ht *getnext_ht = NULL;
assert(op_tree && op_node);
assert((node_when_p && node_types_p && meta_types_p && ext_node_p && ext_val_p) ||
@ -2068,23 +2293,34 @@ _lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struc
dep_tree = tree_sibling;
}
if (int_opts & LYD_INTOPT_REPLY) {
/* add output children defaults */
rc = lyd_new_implicit_r(op_node, lyd_node_child_p(op_node), NULL, NULL, node_when_p, node_types_p,
ext_node_p, LYD_IMPLICIT_OUTPUT, diff);
LY_CHECK_GOTO(rc, cleanup);
/* create the getnext hash table for this module */
rc = lyd_val_getnext_ht_new(&getnext_ht);
LY_CHECK_GOTO(rc, cleanup);
if (int_opts & LYD_INTOPT_REPLY) {
if (validate_subtree) {
/* add output children defaults */
rc = lyd_new_implicit(op_node, lyd_node_child_p(op_node), NULL, NULL, node_when_p, node_types_p,
ext_node_p, LYD_IMPLICIT_OUTPUT, getnext_ht, diff);
LY_CHECK_GOTO(rc, cleanup);
/* skip validating the operation itself, go to children directly */
LY_LIST_FOR(lyd_child(op_node), child) {
rc = lyd_validate_subtree(child, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p, 0, diff);
rc = lyd_validate_subtree(child, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p, 0,
int_opts, getnext_ht, diff);
LY_CHECK_GOTO(rc, cleanup);
}
} else {
/* add output children defaults and their descendants */
rc = lyd_new_implicit_r(op_node, lyd_node_child_p(op_node), NULL, NULL, node_when_p, node_types_p,
ext_node_p, LYD_IMPLICIT_OUTPUT, getnext_ht, diff);
LY_CHECK_GOTO(rc, cleanup);
}
} else {
if (validate_subtree) {
/* prevalidate whole operation subtree */
rc = lyd_validate_subtree(op_node, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p, 0, diff);
rc = lyd_validate_subtree(op_node, node_when_p, node_types_p, meta_types_p, ext_node_p, ext_val_p, 0,
int_opts, getnext_ht, diff);
LY_CHECK_GOTO(rc, cleanup);
}
}
@ -2099,7 +2335,8 @@ _lyd_validate_op(struct lyd_node *op_tree, struct lyd_node *op_node, const struc
LY_CHECK_GOTO(rc = lyd_validate_must(op_node, 0, int_opts, LYXP_IGNORE_WHEN), cleanup);
/* final validation of all the descendants */
rc = lyd_validate_final_r(lyd_child(op_node), op_node, op_node->schema, NULL, 0, int_opts, LYXP_IGNORE_WHEN);
rc = lyd_validate_final_r(lyd_child(op_node), op_node, op_node->schema, NULL, NULL, 0, int_opts, LYXP_IGNORE_WHEN,
getnext_ht);
LY_CHECK_GOTO(rc, cleanup);
cleanup:
@ -2120,6 +2357,7 @@ cleanup:
ly_set_erase(&meta_types, NULL);
ly_set_erase(&ext_node, free);
ly_set_erase(&ext_val, free);
lyd_val_getnext_ht_free(getnext_ht);
return rc;
}

View file

@ -27,6 +27,48 @@ struct lyd_node;
struct lys_module;
struct lysc_node;
/**
* @brief Cached getnext schema nodes stored in a validation HT.
*/
struct lyd_val_getnext {
const struct lysc_node *sparent; /**< schema parent, NULL for top-level nodes */
const struct lysc_node **snodes; /**< array of schema node children excluding choices terminated by NULL */
const struct lysc_node **choices; /**< array of choice schema node children terminated by NULL */
};
/**
* @brief Create a getnext cached schema node validation HT.
*
* @param[out] getnext_ht_p Created getnext HT.
* @return LY_ERR value.
*/
LY_ERR lyd_val_getnext_ht_new(struct ly_ht **getnext_ht_p);
/**
* @brief Free a getnext cached schema node validation HT.
*
* @param[in] getnext_ht Getnext HT to free.
*/
void lyd_val_getnext_ht_free(struct ly_ht *getnext_ht);
/**
* @brief Get the schema children of a schema parent.
*
* Getnext structure cannot be returned because the pointer may become invalid on HT resize.
*
* @param[in] sparent Schema parent to use.
* @param[in] mod Module to use.
* @param[in] ext Extension instance to use, if relevant.
* @param[in] output Whether to traverse operation output instead of input nodes.
* @param[in,out] getnext_ht Getnext HT to use, new @p sparent is added to it.
* @param[out] choices Array of getnext choices of @p sparent.
* @param[out] snodes Array of getnext schema nodes except for choices of @p sparent.
* @return LY_ERR value.
*/
LY_ERR lyd_val_getnext_get(const struct lysc_node *sparent, const struct lys_module *mod,
const struct lysc_ext_instance *ext, ly_bool output, struct ly_ht *getnext_ht, const struct lysc_node ***choices,
const struct lysc_node ***snodes);
/**
* @brief Add new changes into a diff. They are always merged.
*
@ -69,12 +111,16 @@ LY_ERR lyd_validate_unres(struct lyd_node **tree, const struct lys_module *mod,
* @param[in,out] first First sibling.
* @param[in] sparent Schema parent of the siblings, NULL for top-level siblings.
* @param[in] mod Module of the siblings, NULL for nested siblings.
* @param[in] ext Extension instance to use, if relevant.
* @param[in] val_opts Validation options.
* @param[in] int_opts Internal parser options.
* @param[in,out] getnext_ht Getnext HT to use, new @p sparent is added to it.
* @param[in,out] diff Validation diff.
* @return LY_ERR value.
*/
LY_ERR lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
uint32_t val_opts, struct lyd_node **diff);
const struct lysc_ext_instance *ext, uint32_t val_opts, uint32_t int_opts, struct ly_ht *getnext_ht,
struct lyd_node **diff);
/**
* @brief Validate data node with an extension instance, if any, by storing it in its unres set.
@ -105,4 +151,24 @@ LY_ERR lyd_validate(struct lyd_node **tree, const struct lys_module *module, con
ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff);
/**
* @brief Validate a data tree of an extension instance, which is assumed to be a separate data tree independent of
* normal YANG data.
*
* @param[in,out] tree Data tree to validate, nodes may be autodeleted.
* @param[in] ext Extension instance whose data to validate.
* @param[in] val_opts Validation options, see @ref datavalidationoptions.
* @param[in] validate_subtree Whether subtree was already validated (as part of data parsing) or not (separate validation).
* @param[in] node_when_p Set of nodes with when conditions, if NULL a local set is used.
* @param[in] node_types_p Set of unres node types, if NULL a local set is used.
* @param[in] meta_types_p Set of unres metadata types, if NULL a local set is used.
* @param[in] ext_node_p Set of unres nodes with extensions to validate, if NULL a local set is used.
* @param[in] ext_val_p Set of unres extension data to validate, if NULL a local set is used.
* @param[out] diff Generated validation diff, not generated if NULL.
* @return LY_ERR value.
*/
LY_ERR lyd_validate_ext(struct lyd_node **tree, const struct lysc_ext_instance *ext, uint32_t val_opts,
ly_bool validate_subtree, struct ly_set *node_when_p, struct ly_set *node_types_p, struct ly_set *meta_types_p,
struct ly_set *ext_node_p, struct ly_set *ext_val_p, struct lyd_node **diff);
#endif /* LY_VALIDATION_H_ */

View file

@ -12,6 +12,7 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE /* asprintf, strdup */
#define _DEFAULT_SOURCE /* fmodl */
#include "xpath.h"
@ -4098,6 +4099,29 @@ cleanup:
return ret;
}
/**
* @brief Get the module of an identity used in derived-from(-or-self)() functions.
*
* @param[in,out] qname Qualified node name. If includes prefix, it is skipped.
* @param[in,out] qname_len Length of @p qname, is updated accordingly.
* @param[in] set Set with general XPath context.
* @param[out] mod Module of the identity.
* @return LY_ERR
*/
static LY_ERR
xpath_derived_ident_module(const char **qname, uint32_t *qname_len, const struct lyxp_set *set,
const struct lys_module **mod)
{
LY_CHECK_RET(moveto_resolve_model(qname, qname_len, set, set->cur_node ? set->cur_node->schema : NULL, mod));
if (!*mod) {
/* unprefixed JSON identity */
assert(set->format == LY_VALUE_JSON);
*mod = set->cur_mod;
}
return LY_SUCCESS;
}
static LY_ERR
xpath_derived_(struct lyxp_set **args, struct lyxp_set *set, uint32_t options, ly_bool self_match, const char *func)
{
@ -4147,12 +4171,8 @@ xpath_derived_(struct lyxp_set **args, struct lyxp_set *set, uint32_t options, l
/* parse the identity */
id_name = args[1]->val.str;
id_len = strlen(id_name);
rc = moveto_resolve_model(&id_name, &id_len, set, set->cur_node ? set->cur_node->schema : NULL, &mod);
rc = xpath_derived_ident_module(&id_name, &id_len, set, &mod);
LY_CHECK_RET(rc);
if (!mod) {
LOGVAL(set->ctx, LYVE_XPATH, "Identity \"%.*s\" without a prefix.", (int)id_len, id_name);
return LY_EVALID;
}
/* find the identity */
found = 0;
@ -5631,14 +5651,14 @@ xpath_pi_text(struct lyxp_set *set, enum lyxp_axis axis, uint32_t options)
}
/**
* @brief Skip prefix and return corresponding model if there is a prefix. Logs directly.
* @brief Skip prefix and return corresponding model. Logs directly.
*
* XPath @p set is expected to be a (sc)node set!
*
* @param[in,out] qname Qualified node name. If includes prefix, it is skipped.
* @param[in,out] qname_len Length of @p qname, is updated accordingly.
* @param[in] set Set with general XPath context.
* @param[in] ctx_scnode Context node to inherit module for unprefixed node for ::LY_PREF_JSON.
* @param[in] ctx_scnode Current context schema node (parent).
* @param[out] moveto_mod Expected module of a matching node.
* @return LY_ERR
*/
@ -5683,6 +5703,7 @@ moveto_resolve_model(const char **qname, uint32_t *qname_len, const struct lyxp_
if (ctx_scnode) {
mod = ctx_scnode->module;
} else {
/* JSON XPath is our own format (except for identityref), which supports node names matching all the modules */
mod = NULL;
}
break;
@ -7455,7 +7476,7 @@ moveto_op_math(struct lyxp_set *set1, struct lyxp_set *set2, const char *op)
/* 'mod' */
case 'm':
set1->val.num = ((long long)set1->val.num) % ((long long)set2->val.num);
set1->val.num = fmodl(set1->val.num, set2->val.num);
break;
default:
@ -7677,7 +7698,7 @@ eval_name_test_try_compile_predicate_key(const char *nametest, uint32_t len, con
/* prefix (module) */
LY_CHECK_RET(moveto_resolve_model(&nametest, &len, set, ctx_scnode, &mod));
if (mod != key->module) {
if (mod && (mod != key->module)) {
return LY_ENOT;
}

View file

@ -379,7 +379,7 @@ LY_ERR lyxp_atomize(const struct ly_ctx *ctx, const struct lyxp_expr *exp, const
#define LYXP_SCNODE 0x04 /**< No special tree access modifiers. */
#define LYXP_SCNODE_SCHEMA LYS_FIND_XP_SCHEMA /**< Apply node access restrictions defined for 'when' and 'must' evaluation. */
#define LYXP_SCNODE_OUTPUT LYS_FIND_XP_OUTPUT /**< Search RPC/action output nodes instead of input ones. */
#define LYXP_SCNODE_ALL 0x1C /**< mask for all the LYXP_* values */
#define LYXP_SCNODE_ALL 0x1C /**< mask for all the LYXP_SCNODE_* values */
#define LYXP_SKIP_EXPR 0x20 /**< The rest of the expression will not be evaluated (lazy evaluation) */
#define LYXP_SCNODE_ERROR LYS_FIND_NO_MATCH_ERROR /**< Return error if a path segment matches no nodes, otherwise only
warning is printed. */

View file

@ -135,8 +135,7 @@ proc ly_completion {input output} {
global prompt
send -- "${input}\t"
# expecting echoing input, output and 10 terminal control characters
expect -re "^${input}\r${prompt}${output}.*\r.*$"
expect -re "${input}${output}"
}
# Send a completion request and check if the anchored regex hint options match.
@ -146,11 +145,13 @@ proc ly_hint {input prev_input hints} {
set output {}
foreach i $hints {
# each element might have some number of spaces and CRLF around it
append output "${i} *(?:\\r\\n)?"
append output "${i} *(\\r\\n)?"
}
set termcode1 "\r\\u001b\\\[0K"
set termcode2 ".*"
send -- "${input}\t"
# expecting the hints, previous input from which the hints were generated
# and some number of terminal control characters
expect -re "${output}\r${prompt}${prev_input}.*\r.*$"
expect -re "${output}${termcode1}${prompt}${prev_input}${termcode2}"
}

View file

@ -65,6 +65,13 @@ const char *schema_a =
" fraction-digits 5;\n"
" }\n"
" }\n"
" leaf foo5 {\n"
" type decimal64 {\n"
" fraction-digits 1;\n"
" range \"0.5 .. 99.5\";\n"
" }\n"
" must \"(. - 0.5) mod 0.5 = 0\";\n"
" }\n"
" container c {\n"
" leaf x {\n"
" type string;\n"
@ -519,7 +526,7 @@ test_atomize(void **state)
/* some random paths just making sure the API function works */
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*", 0, &set));
assert_int_equal(7, set->count);
assert_int_equal(8, set->count);
ly_set_free(set, NULL);
/* all nodes from all modules (including internal, which can change easily, so check just the test modules) */
@ -536,7 +543,7 @@ test_atomize(void **state)
ly_set_free(set, NULL);
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/*", 0, &set));
assert_int_equal(14, set->count);
assert_int_equal(15, set->count);
ly_set_free(set, NULL);
/*
@ -570,7 +577,7 @@ test_atomize(void **state)
/* descendant-or-self */
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/a:*/descendant-or-self::c", 0, &set));
assert_int_equal(8, set->count);
assert_int_equal(9, set->count);
ly_set_free(set, NULL);
/* following */
@ -585,7 +592,7 @@ test_atomize(void **state)
/* parent */
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:*/c/parent::l1", 0, &set));
assert_int_equal(8, set->count);
assert_int_equal(9, set->count);
ly_set_free(set, NULL);
assert_int_equal(LY_SUCCESS, lys_find_xpath_atoms(UTEST_LYCTX, NULL, "/child::a:c//..", 0, &set));
@ -1240,6 +1247,20 @@ test_trim(void **state)
lyd_free_all(tree);
}
static void
test_mod(void **state)
{
const char *data;
struct lyd_node *tree;
data = "<foo5 xmlns=\"urn:tests:a\">10.5</foo5>";
/* parse and validate */
assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
assert_non_null(tree);
lyd_free_siblings(tree);
}
int
main(void)
{
@ -1257,6 +1278,7 @@ main(void)
UTEST(test_variables, setup),
UTEST(test_axes, setup),
UTEST(test_trim, setup),
UTEST(test_mod, setup),
};
return cmocka_run_group_tests(tests, NULL, NULL);

View file

@ -353,6 +353,14 @@ test_path(void **state)
assert_int_equal(ret, LY_SUCCESS);
assert_non_null(node);
/* too high index */
ret = lyd_new_path2(root, NULL, "/a:c2/l3[8]", NULL, 0, 0, 0, NULL, &node);
assert_int_equal(ret, LY_EINVAL);
CHECK_LOG_CTX("Cannot create \"l3\" on position 8, only 6 instances exist.", NULL, 0);
ret = lyd_new_path2(root, NULL, "/a:l2[2]", NULL, 0, 0, 0, NULL, &node);
assert_int_equal(ret, LY_EINVAL);
CHECK_LOG_CTX("Cannot create \"l2\" on position 2, no instances exist.", NULL, 0);
lyd_print_mem(&str, root, LYD_XML, LYD_PRINT_WITHSIBLINGS);
assert_string_equal(str,
"<c2 xmlns=\"urn:tests:a\">\n"

View file

@ -1605,21 +1605,22 @@ test_order_violation(void **state)
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
/* inserting a new node causes the nodes to be sorted */
data = "{\"a:ll\":[1,8,2]}";
data = "{\"a:ll\":[8,2,1]}";
CHECK_PARSE_LYD_PARAM(data, LYD_JSON, LYD_PARSE_ORDERED, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
assert_true(tree && !tree->meta && tree->next && tree->next->next);
assert_string_equal(lyd_get_value(tree), "1");
assert_string_equal(lyd_get_value(tree->next), "8");
assert_string_equal(lyd_get_value(tree->next->next), "2");
#ifndef NDEBUG
CHECK_LOG_CTX("Data in \"ll\" are not sorted, inserted node should not be added to the end.", NULL, 0);
CHECK_LOG_CTX("Data in \"ll\" are not sorted, inserted node should not be added to the end.", NULL, 0);
#endif
assert_int_equal(lyd_new_term(NULL, mod, "ll", "3", 0, &node), LY_SUCCESS);
lyd_insert_sibling(tree, node, NULL);
assert_string_equal(lyd_get_value(tree), "1");
assert_true(tree && !tree->meta && tree->next && tree->next->next);
assert_string_equal(lyd_get_value(tree), "8");
assert_string_equal(lyd_get_value(tree->next), "2");
assert_string_equal(lyd_get_value(tree->next->next), "3");
assert_string_equal(lyd_get_value(tree->next->next->next), "8");
assert_string_equal(lyd_get_value(tree->next->next), "1");
assert_int_equal(lyd_new_term(NULL, mod, "ll", "3", 0, &node), LY_SUCCESS);
lyd_insert_sibling(tree, node, &first);
assert_string_equal(lyd_get_value(first), "1");
assert_string_equal(lyd_get_value(first->next), "2");
assert_string_equal(lyd_get_value(first->next->next), "3");
assert_string_equal(lyd_get_value(first->next->next->next), "8");
lyd_free_all(tree);
/* move unsorted nodes causes the nodes to be sorted */

View file

@ -163,6 +163,37 @@ test_type_incomplete_when(void **state)
lyd_free_all(tree);
}
static void
test_unprefixed_ident(void **state)
{
struct lyd_node *tree;
const char *schema =
"module a {\n"
" namespace urn:tests:a;\n"
" prefix a;\n"
" yang-version 1.1;\n"
"\n"
" identity d3 {base d2;}\n"
" identity d2 {base d1;}\n"
" identity d1;\n"
"\n"
" leaf a {type identityref {base d1;}}\n"
" leaf b {type string; must \"derived-from-or-self(/a, 'd2')\";}\n"
" leaf c {type string; when \"derived-from(/a, 'd2')\";}\n"
"}";
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
CHECK_PARSE_LYD_PARAM("<b xmlns=\"urn:tests:a\">hey</b>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
CHECK_LOG_CTX("Must condition \"derived-from-or-self(/a, 'd2')\" not satisfied.", "/a:b", 0);
CHECK_PARSE_LYD_PARAM("<c xmlns=\"urn:tests:a\">hey</c>", LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
CHECK_LOG_CTX("When condition \"derived-from(/a, 'd2')\" not satisfied.", "/a:c", 0);
LYD_TREE_CREATE("<a xmlns=\"urn:tests:a\">d3</a><b xmlns=\"urn:tests:a\">b-val</b><c xmlns=\"urn:tests:a\">c-val</c>", tree);
lyd_free_all(tree);
}
static void
test_mandatory(void **state)
{
@ -1515,6 +1546,20 @@ test_case(void **state)
CHECK_LOG_CTX("Data for both cases \"v0\" and \"v2\" exist.", "/k:ch", 6);
}
static void
test_pattern(void **UNUSED(state))
{
pcre2_code *pcode = NULL;
assert_int_equal(ly_pattern_match(NULL, "a.b.c", "abc", 0, NULL), LY_ENOT);
assert_int_equal(ly_pattern_match(NULL, "a.b.c", "a0b1c", 0, NULL), LY_SUCCESS);
assert_int_equal(ly_pattern_match(NULL, "a.b.c", "abc", 0, &pcode), LY_ENOT);
assert_int_equal(ly_pattern_match(NULL, NULL, "a0b1c", 0, &pcode), LY_SUCCESS);
pcre2_code_free(pcode);
}
int
main(void)
{
@ -1523,6 +1568,7 @@ main(void)
UTEST(test_mandatory),
UTEST(test_mandatory_when),
UTEST(test_type_incomplete_when),
UTEST(test_unprefixed_ident),
UTEST(test_minmax),
UTEST(test_unique),
UTEST(test_unique_nested),
@ -1535,6 +1581,7 @@ main(void)
UTEST(test_rpc),
UTEST(test_reply),
UTEST(test_case),
UTEST(test_pattern),
};
return cmocka_run_group_tests(tests, NULL, NULL);

View file

@ -1,4 +1,4 @@
if(WIN32)
if(WIN32 OR NOT ENABLE_YANGLINT_INTERACTIVE)
set(YANGLINT_INTERACTIVE OFF)
else()
set(YANGLINT_INTERACTIVE ON)

View file

@ -12,14 +12,15 @@ test completion_hints_ietf_ip {Completion and hints for ietf-ip.yang} {
ly_cmd "add $mdir/ietf-ip.yang"
# completion and hint
ly_completion "print -f info -P " "print -f info -P /ietf-"
ly_completion "print -f info -P " "/ietf-"
set hints {"/ietf-yang-schema-mount:schema-mounts" "/ietf-interfaces:interfaces" "/ietf-interfaces:interfaces-state"}
ly_hint "" "print -f info -P /ietf-" $hints
# double completion
ly_completion "i" "print -f info -P /ietf-interfaces:interfaces"
ly_completion "/" "print -f info -P /ietf-interfaces:interfaces/interface"
ly_completion "i" "nterfaces:interfaces"
ly_completion "/" "interface"
# current cli: print -f info -P /ietf-interfaces:interfaces/interface
# a lot of hints
set hints {"/ietf-interfaces:interfaces/interface"
@ -31,16 +32,20 @@ test completion_hints_ietf_ip {Completion and hints for ietf-ip.yang} {
ly_hint "" "print -f info -P /ietf-interfaces:interfaces/interface" $hints
# double tab
ly_completion "/i" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv"
ly_completion "4" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4"
ly_completion "/i" "etf-ip:ipv"
# current cli: print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv
ly_completion "4" ""
# current cli: print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4
set hints { "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled"
"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/forwarding" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/mtu"
"/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/address" "/ietf-interfaces:interfaces/interface/ietf-ip:ipv4/neighbor"
}
ly_hint "\t" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv" $hints
ly_hint "\t" "print -f info -P /ietf-interfaces:interfaces/interface" $hints
# no more completion
ly_completion "/e" "print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled "
ly_completion "/e" "nabled "
# current cli: print -f info -P /ietf-interfaces:interfaces/interface/ietf-ip:ipv4/enabled
}}
# Note that somehow a command is automatically sent again (\t\t replaced by \r) after the hints.

View file

@ -24,7 +24,8 @@ if(ENABLE_YANGLINT_INTERACTIVE)
main.c
completion.c
configuration.c
linenoise/linenoise.c)
linenoise/linenoise.c
linenoise/utf8.c)
else()
set(lintsrc ${lintsrc}
main_ni_only.c)

View file

@ -15,11 +15,13 @@
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L /* strdup */
#include <dirent.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "libyang.h"
@ -28,6 +30,70 @@
#include "compat.h"
#include "linenoise/linenoise.h"
/**
* @brief Fill path completion.
*
* @param[in] hint Path to directory.
* @param[in] lc For adding completions.
*/
static void
path_completion(const char *hint, linenoiseCompletions *lc)
{
const char *ptr;
char *full_path, *hint_ptr, match[FILENAME_MAX + 2];
DIR *dir;
struct dirent *ent;
struct stat st;
lc->path = 1;
ptr = strrchr(hint, '/');
/* new relative path */
if (ptr == NULL) {
full_path = malloc(2 + FILENAME_MAX + 1);
strcpy(full_path, "./");
ptr = hint;
} else {
full_path = malloc((int)(ptr - hint) + FILENAME_MAX + 1);
++ptr;
sprintf(full_path, "%.*s", (int)(ptr - hint), hint);
}
hint_ptr = full_path + strlen(full_path);
dir = opendir(full_path);
if (dir == NULL) {
free(full_path);
return;
}
while ((ent = readdir(dir))) {
if (ent->d_name[0] == '.') {
continue;
}
if (!strncmp(ptr, ent->d_name, strlen(ptr))) {
/* is it a directory? */
strcpy(hint_ptr, ent->d_name);
if (stat(full_path, &st)) {
/* skip this item */
continue;
}
strcpy(match, ent->d_name);
if (S_ISDIR(st.st_mode)) {
strcat(match, "/");
}
linenoiseAddCompletion(lc, match);
}
}
free(full_path);
closedir(dir);
}
/* from the main.c */
extern struct ly_ctx *ctx;
@ -430,29 +496,28 @@ complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
struct autocomplete {
enum COMMAND_INDEX ci; /**< command index to global variable 'commands' */
const char *opt; /**< optional option */
void (*ln_cb)(const char *, const char *, linenoiseCompletions *); /**< linenoise callback to call */
void (*yl_cb)(const char *, char ***, unsigned int *); /**< yanglint callback to call */
void (*yl_cb)(const char *, char ***, unsigned int *); /**< yanglint callback to call */
} ac[] = {
{CMD_ADD, NULL, linenoisePathCompletion, NULL},
{CMD_PRINT, "-f", NULL, get_print_format_arg},
{CMD_PRINT, "-P", NULL, get_schema_completion},
{CMD_PRINT, "-o", linenoisePathCompletion, NULL},
{CMD_PRINT, NULL, NULL, get_model_completion},
{CMD_SEARCHPATH, NULL, linenoisePathCompletion, NULL},
{CMD_EXTDATA, NULL, linenoisePathCompletion, NULL},
{CMD_CLEAR, "-Y", linenoisePathCompletion, NULL},
{CMD_DATA, "-t", NULL, get_data_type_arg},
{CMD_DATA, "-O", linenoisePathCompletion, NULL},
{CMD_DATA, "-R", linenoisePathCompletion, NULL},
{CMD_DATA, "-f", NULL, get_data_in_format_arg},
{CMD_DATA, "-F", NULL, get_data_in_format_arg},
{CMD_DATA, "-d", NULL, get_data_default_arg},
{CMD_DATA, "-o", linenoisePathCompletion, NULL},
{CMD_DATA, NULL, linenoisePathCompletion, NULL},
{CMD_LIST, NULL, NULL, get_list_format_arg},
{CMD_FEATURE, NULL, NULL, get_model_completion},
{CMD_VERB, NULL, NULL, get_verb_arg},
{CMD_DEBUG, NULL, NULL, get_debug_arg},
{CMD_ADD, NULL, NULL},
{CMD_PRINT, "-f", get_print_format_arg},
{CMD_PRINT, "-P", get_schema_completion},
{CMD_PRINT, "-o", NULL},
{CMD_PRINT, NULL, get_model_completion},
{CMD_SEARCHPATH, NULL, NULL},
{CMD_EXTDATA, NULL, NULL},
{CMD_CLEAR, "-Y", NULL},
{CMD_DATA, "-t", get_data_type_arg},
{CMD_DATA, "-O", NULL},
{CMD_DATA, "-R", NULL},
{CMD_DATA, "-f", get_data_in_format_arg},
{CMD_DATA, "-F", get_data_in_format_arg},
{CMD_DATA, "-d", get_data_default_arg},
{CMD_DATA, "-o", NULL},
{CMD_DATA, NULL, NULL},
{CMD_LIST, NULL, get_list_format_arg},
{CMD_FEATURE, NULL, get_model_completion},
{CMD_VERB, NULL, get_verb_arg},
{CMD_DEBUG, NULL, get_debug_arg},
};
size_t name_len;
const char *last, *name, *getoptstr;
@ -490,10 +555,10 @@ complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
}
/* callback */
if (ac[i].ln_cb) {
ac[i].ln_cb(buf, hint, lc);
} else {
if (ac[i].yl_cb) {
ac[i].yl_cb(hint, &matches, &match_count);
} else {
path_completion(hint, lc);
}
break;
}

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,7 @@
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
@ -39,13 +39,21 @@
#ifndef __LINENOISE_H
#define __LINENOISE_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h> /* For size_t. */
extern char *linenoiseEditMore;
/* The linenoiseState structure represents the state during line editing.
* We pass this state to functions implementing specific editing
* functionalities. */
struct linenoiseState {
int in_completion; /* The user pressed TAB and we are now in completion
* mode, so input is handled by completeLine(). */
size_t completion_idx; /* Index of next completion to propose. */
int ifd; /* Terminal stdin file descriptor. */
int ofd; /* Terminal stdout file descriptor. */
char *buf; /* Edited line buffer. */
@ -53,39 +61,62 @@ struct linenoiseState {
const char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
size_t oldcolpos; /* Previous refresh cursor column position. */
size_t oldcollen; /* Previous length of buffer. */
size_t len; /* Current edited line length. */
size_t cols; /* Number of columns in terminal. */
size_t maxrows; /* Maximum num of rows used so far (multiline mode) */
int rawmode;
size_t oldrows; /* Rows used by last refrehsed line (multiline mode) */
int history_index; /* The history index we are currently editing. */
int prev_history_index; /* Previous history index. */
};
extern struct linenoiseState lss;
typedef struct linenoiseCompletions {
int path;
size_t len;
char **cvec;
} linenoiseCompletions;
/* Non blocking API. */
int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt);
char *linenoiseEditFeed(struct linenoiseState *l);
void linenoiseEditStop(struct linenoiseState *l);
void linenoiseHide(struct linenoiseState *l);
void linenoiseShow(struct linenoiseState *l);
/* Blocking API. */
char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
/* Completion API. */
typedef void(linenoiseCompletionCallback)(const char *, const char *, linenoiseCompletions *);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
typedef void(linenoiseFreeHintsCallback)(void *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
char *linenoise(const char *prompt);
/* History API. */
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
/* Other utilities. */
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);
void linenoiseMaskModeEnable(void);
void linenoiseMaskModeDisable(void);
void linenoisePathCompletion(const char *, const char *, linenoiseCompletions *);
void linenoiseRefreshLine(void);
int linenoiseEnableRawMode(int fd);
void linenoiseDisableRawMode(int fd);
typedef size_t (linenoisePrevCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len);
typedef size_t (linenoiseNextCharLen)(const char *buf, size_t buf_len, size_t pos, size_t *col_len);
typedef size_t (linenoiseReadCode)(int fd, char *buf, size_t buf_len, int* c);
void linenoiseSetEncodingFunctions(
linenoisePrevCharLen *prevCharLenFunc,
linenoiseNextCharLen *nextCharLenFunc,
linenoiseReadCode *readCodeFunc);
#ifdef __cplusplus
}

464
tools/lint/linenoise/utf8.c Normal file
View file

@ -0,0 +1,464 @@
/* encoding/utf8.c -- VERSION 1.0
*
* Guerrilla line editing library against the idea that a line editing lib
* needs to be 20,000 lines of C code.
*
* You can find the latest source code at:
*
* http://github.com/antirez/linenoise
*
* Does a number of crazy assumptions that happen to be true in 99.9999% of
* the 2010 UNIX computers around.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE /* required for compat.h before including any other headers */
#include <unistd.h>
#include <stdio.h>
#include "compat.h"
/* ============================ UTF8 utilities ============================== */
static unsigned long wideCharTable[][2] = {
{ 0x1100, 0x115F }, { 0x231A, 0x231B }, { 0x2329, 0x232A }, { 0x23E9, 0x23EC },
{ 0x23F0, 0x23F0 }, { 0x23F3, 0x23F3 }, { 0x25FD, 0x25FE }, { 0x2614, 0x2615 },
{ 0x2648, 0x2653 }, { 0x267F, 0x267F }, { 0x2693, 0x2693 }, { 0x26A1, 0x26A1 },
{ 0x26AA, 0x26AB }, { 0x26BD, 0x26BE }, { 0x26C4, 0x26C5 }, { 0x26CE, 0x26CE },
{ 0x26D4, 0x26D4 }, { 0x26EA, 0x26EA }, { 0x26F2, 0x26F3 }, { 0x26F5, 0x26F5 },
{ 0x26FA, 0x26FA }, { 0x26FD, 0x26FD }, { 0x2705, 0x2705 }, { 0x270A, 0x270B },
{ 0x2728, 0x2728 }, { 0x274C, 0x274C }, { 0x274E, 0x274E }, { 0x2753, 0x2755 },
{ 0x2757, 0x2757 }, { 0x2795, 0x2797 }, { 0x27B0, 0x27B0 }, { 0x27BF, 0x27BF },
{ 0x2B1B, 0x2B1C }, { 0x2B50, 0x2B50 }, { 0x2B55, 0x2B55 }, { 0x2E80, 0x2E99 },
{ 0x2E9B, 0x2EF3 }, { 0x2F00, 0x2FD5 }, { 0x2FF0, 0x2FFB }, { 0x3000, 0x303E },
{ 0x3041, 0x3096 }, { 0x3099, 0x30FF }, { 0x3105, 0x312F }, { 0x3131, 0x318E },
{ 0x3190, 0x31E3 }, { 0x31F0, 0x321E }, { 0x3220, 0x3247 }, { 0x3250, 0x4DBF },
{ 0x4E00, 0xA48C }, { 0xA490, 0xA4C6 }, { 0xA960, 0xA97C }, { 0xAC00, 0xD7A3 },
{ 0xF900, 0xFAFF }, { 0xFE10, 0xFE19 }, { 0xFE30, 0xFE52 }, { 0xFE54, 0xFE66 },
{ 0xFE68, 0xFE6B }, { 0xFF01, 0xFF60 }, { 0xFFE0, 0xFFE6 }, { 0x16FE0, 0x16FE4 },
{ 0x16FF0, 0x16FF1 }, { 0x17000, 0x187F7 }, { 0x18800, 0x18CD5 }, { 0x18D00, 0x18D08 },
{ 0x1AFF0, 0x1AFF3 }, { 0x1AFF5, 0x1AFFB }, { 0x1AFFD, 0x1AFFE }, { 0x1B000, 0x1B122 },
{ 0x1B150, 0x1B152 }, { 0x1B164, 0x1B167 }, { 0x1B170, 0x1B2FB }, { 0x1F004, 0x1F004 },
{ 0x1F0CF, 0x1F0CF }, { 0x1F18E, 0x1F18E }, { 0x1F191, 0x1F19A }, { 0x1F200, 0x1F202 },
{ 0x1F210, 0x1F23B }, { 0x1F240, 0x1F248 }, { 0x1F250, 0x1F251 }, { 0x1F260, 0x1F265 },
{ 0x1F300, 0x1F320 }, { 0x1F32D, 0x1F335 }, { 0x1F337, 0x1F37C }, { 0x1F37E, 0x1F393 },
{ 0x1F3A0, 0x1F3CA }, { 0x1F3CF, 0x1F3D3 }, { 0x1F3E0, 0x1F3F0 }, { 0x1F3F4, 0x1F3F4 },
{ 0x1F3F8, 0x1F43E }, { 0x1F440, 0x1F440 }, { 0x1F442, 0x1F4FC }, { 0x1F4FF, 0x1F53D },
{ 0x1F54B, 0x1F54E }, { 0x1F550, 0x1F567 }, { 0x1F57A, 0x1F57A }, { 0x1F595, 0x1F596 },
{ 0x1F5A4, 0x1F5A4 }, { 0x1F5FB, 0x1F64F }, { 0x1F680, 0x1F6C5 }, { 0x1F6CC, 0x1F6CC },
{ 0x1F6D0, 0x1F6D2 }, { 0x1F6D5, 0x1F6D7 }, { 0x1F6DD, 0x1F6DF }, { 0x1F6EB, 0x1F6EC },
{ 0x1F6F4, 0x1F6FC }, { 0x1F7E0, 0x1F7EB }, { 0x1F7F0, 0x1F7F0 }, { 0x1F90C, 0x1F93A },
{ 0x1F93C, 0x1F945 }, { 0x1F947, 0x1F9FF }, { 0x1FA70, 0x1FA74 }, { 0x1FA78, 0x1FA7C },
{ 0x1FA80, 0x1FA86 }, { 0x1FA90, 0x1FAAC }, { 0x1FAB0, 0x1FABA }, { 0x1FAC0, 0x1FAC5 },
{ 0x1FAD0, 0x1FAD9 }, { 0x1FAE0, 0x1FAE7 }, { 0x1FAF0, 0x1FAF6 }, { 0x20000, 0x2FFFD },
{ 0x30000, 0x3FFFD },
};
static size_t wideCharTableSize = sizeof(wideCharTable) / sizeof(wideCharTable[0]);
static unsigned long combiningCharTable[] = {
0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0591, 0x0592, 0x0593,
0x0594, 0x0595, 0x0596, 0x0597, 0x0598, 0x0599, 0x059A, 0x059B,
0x059C, 0x059D, 0x059E, 0x059F, 0x05A0, 0x05A1, 0x05A2, 0x05A3,
0x05A4, 0x05A5, 0x05A6, 0x05A7, 0x05A8, 0x05A9, 0x05AA, 0x05AB,
0x05AC, 0x05AD, 0x05AE, 0x05AF, 0x05B0, 0x05B1, 0x05B2, 0x05B3,
0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0x05BA, 0x05BB,
0x05BC, 0x05BD, 0x05BF, 0x05C1, 0x05C2, 0x05C4, 0x05C5, 0x05C7,
0x0610, 0x0611, 0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617,
0x0618, 0x0619, 0x061A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F,
0x0650, 0x0651, 0x0652, 0x0653, 0x0654, 0x0655, 0x0656, 0x0657,
0x0658, 0x0659, 0x065A, 0x065B, 0x065C, 0x065D, 0x065E, 0x065F,
0x0670, 0x06D6, 0x06D7, 0x06D8, 0x06D9, 0x06DA, 0x06DB, 0x06DC,
0x06DF, 0x06E0, 0x06E1, 0x06E2, 0x06E3, 0x06E4, 0x06E7, 0x06E8,
0x06EA, 0x06EB, 0x06EC, 0x06ED, 0x0711, 0x0730, 0x0731, 0x0732,
0x0733, 0x0734, 0x0735, 0x0736, 0x0737, 0x0738, 0x0739, 0x073A,
0x073B, 0x073C, 0x073D, 0x073E, 0x073F, 0x0740, 0x0741, 0x0742,
0x0743, 0x0744, 0x0745, 0x0746, 0x0747, 0x0748, 0x0749, 0x074A,
0x07A6, 0x07A7, 0x07A8, 0x07A9, 0x07AA, 0x07AB, 0x07AC, 0x07AD,
0x07AE, 0x07AF, 0x07B0, 0x07EB, 0x07EC, 0x07ED, 0x07EE, 0x07EF,
0x07F0, 0x07F1, 0x07F2, 0x07F3, 0x07FD, 0x0816, 0x0817, 0x0818,
0x0819, 0x081B, 0x081C, 0x081D, 0x081E, 0x081F, 0x0820, 0x0821,
0x0822, 0x0823, 0x0825, 0x0826, 0x0827, 0x0829, 0x082A, 0x082B,
0x082C, 0x082D, 0x0859, 0x085A, 0x085B, 0x0898, 0x0899, 0x089A,
0x089B, 0x089C, 0x089D, 0x089E, 0x089F, 0x08CA, 0x08CB, 0x08CC,
0x08CD, 0x08CE, 0x08CF, 0x08D0, 0x08D1, 0x08D2, 0x08D3, 0x08D4,
0x08D5, 0x08D6, 0x08D7, 0x08D8, 0x08D9, 0x08DA, 0x08DB, 0x08DC,
0x08DD, 0x08DE, 0x08DF, 0x08E0, 0x08E1, 0x08E3, 0x08E4, 0x08E5,
0x08E6, 0x08E7, 0x08E8, 0x08E9, 0x08EA, 0x08EB, 0x08EC, 0x08ED,
0x08EE, 0x08EF, 0x08F0, 0x08F1, 0x08F2, 0x08F3, 0x08F4, 0x08F5,
0x08F6, 0x08F7, 0x08F8, 0x08F9, 0x08FA, 0x08FB, 0x08FC, 0x08FD,
0x08FE, 0x08FF, 0x0900, 0x0901, 0x0902, 0x093A, 0x093C, 0x0941,
0x0942, 0x0943, 0x0944, 0x0945, 0x0946, 0x0947, 0x0948, 0x094D,
0x0951, 0x0952, 0x0953, 0x0954, 0x0955, 0x0956, 0x0957, 0x0962,
0x0963, 0x0981, 0x09BC, 0x09C1, 0x09C2, 0x09C3, 0x09C4, 0x09CD,
0x09E2, 0x09E3, 0x09FE, 0x0A01, 0x0A02, 0x0A3C, 0x0A41, 0x0A42,
0x0A47, 0x0A48, 0x0A4B, 0x0A4C, 0x0A4D, 0x0A51, 0x0A70, 0x0A71,
0x0A75, 0x0A81, 0x0A82, 0x0ABC, 0x0AC1, 0x0AC2, 0x0AC3, 0x0AC4,
0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0AE2, 0x0AE3, 0x0AFA, 0x0AFB,
0x0AFC, 0x0AFD, 0x0AFE, 0x0AFF, 0x0B01, 0x0B3C, 0x0B3F, 0x0B41,
0x0B42, 0x0B43, 0x0B44, 0x0B4D, 0x0B55, 0x0B56, 0x0B62, 0x0B63,
0x0B82, 0x0BC0, 0x0BCD, 0x0C00, 0x0C04, 0x0C3C, 0x0C3E, 0x0C3F,
0x0C40, 0x0C46, 0x0C47, 0x0C48, 0x0C4A, 0x0C4B, 0x0C4C, 0x0C4D,
0x0C55, 0x0C56, 0x0C62, 0x0C63, 0x0C81, 0x0CBC, 0x0CBF, 0x0CC6,
0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D00, 0x0D01, 0x0D3B, 0x0D3C,
0x0D41, 0x0D42, 0x0D43, 0x0D44, 0x0D4D, 0x0D62, 0x0D63, 0x0D81,
0x0DCA, 0x0DD2, 0x0DD3, 0x0DD4, 0x0DD6, 0x0E31, 0x0E34, 0x0E35,
0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0x0E47, 0x0E48, 0x0E49,
0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0EB1, 0x0EB4, 0x0EB5,
0x0EB6, 0x0EB7, 0x0EB8, 0x0EB9, 0x0EBA, 0x0EBB, 0x0EBC, 0x0EC8,
0x0EC9, 0x0ECA, 0x0ECB, 0x0ECC, 0x0ECD, 0x0F18, 0x0F19, 0x0F35,
0x0F37, 0x0F39, 0x0F71, 0x0F72, 0x0F73, 0x0F74, 0x0F75, 0x0F76,
0x0F77, 0x0F78, 0x0F79, 0x0F7A, 0x0F7B, 0x0F7C, 0x0F7D, 0x0F7E,
0x0F80, 0x0F81, 0x0F82, 0x0F83, 0x0F84, 0x0F86, 0x0F87, 0x0F8D,
0x0F8E, 0x0F8F, 0x0F90, 0x0F91, 0x0F92, 0x0F93, 0x0F94, 0x0F95,
0x0F96, 0x0F97, 0x0F99, 0x0F9A, 0x0F9B, 0x0F9C, 0x0F9D, 0x0F9E,
0x0F9F, 0x0FA0, 0x0FA1, 0x0FA2, 0x0FA3, 0x0FA4, 0x0FA5, 0x0FA6,
0x0FA7, 0x0FA8, 0x0FA9, 0x0FAA, 0x0FAB, 0x0FAC, 0x0FAD, 0x0FAE,
0x0FAF, 0x0FB0, 0x0FB1, 0x0FB2, 0x0FB3, 0x0FB4, 0x0FB5, 0x0FB6,
0x0FB7, 0x0FB8, 0x0FB9, 0x0FBA, 0x0FBB, 0x0FBC, 0x0FC6, 0x102D,
0x102E, 0x102F, 0x1030, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036,
0x1037, 0x1039, 0x103A, 0x103D, 0x103E, 0x1058, 0x1059, 0x105E,
0x105F, 0x1060, 0x1071, 0x1072, 0x1073, 0x1074, 0x1082, 0x1085,
0x1086, 0x108D, 0x109D, 0x135D, 0x135E, 0x135F, 0x1712, 0x1713,
0x1714, 0x1732, 0x1733, 0x1752, 0x1753, 0x1772, 0x1773, 0x17B4,
0x17B5, 0x17B7, 0x17B8, 0x17B9, 0x17BA, 0x17BB, 0x17BC, 0x17BD,
0x17C6, 0x17C9, 0x17CA, 0x17CB, 0x17CC, 0x17CD, 0x17CE, 0x17CF,
0x17D0, 0x17D1, 0x17D2, 0x17D3, 0x17DD, 0x180B, 0x180C, 0x180D,
0x180F, 0x1885, 0x1886, 0x18A9, 0x1920, 0x1921, 0x1922, 0x1927,
0x1928, 0x1932, 0x1939, 0x193A, 0x193B, 0x1A17, 0x1A18, 0x1A1B,
0x1A56, 0x1A58, 0x1A59, 0x1A5A, 0x1A5B, 0x1A5C, 0x1A5D, 0x1A5E,
0x1A60, 0x1A62, 0x1A65, 0x1A66, 0x1A67, 0x1A68, 0x1A69, 0x1A6A,
0x1A6B, 0x1A6C, 0x1A73, 0x1A74, 0x1A75, 0x1A76, 0x1A77, 0x1A78,
0x1A79, 0x1A7A, 0x1A7B, 0x1A7C, 0x1A7F, 0x1AB0, 0x1AB1, 0x1AB2,
0x1AB3, 0x1AB4, 0x1AB5, 0x1AB6, 0x1AB7, 0x1AB8, 0x1AB9, 0x1ABA,
0x1ABB, 0x1ABC, 0x1ABD, 0x1ABF, 0x1AC0, 0x1AC1, 0x1AC2, 0x1AC3,
0x1AC4, 0x1AC5, 0x1AC6, 0x1AC7, 0x1AC8, 0x1AC9, 0x1ACA, 0x1ACB,
0x1ACC, 0x1ACD, 0x1ACE, 0x1B00, 0x1B01, 0x1B02, 0x1B03, 0x1B34,
0x1B36, 0x1B37, 0x1B38, 0x1B39, 0x1B3A, 0x1B3C, 0x1B42, 0x1B6B,
0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73,
0x1B80, 0x1B81, 0x1BA2, 0x1BA3, 0x1BA4, 0x1BA5, 0x1BA8, 0x1BA9,
0x1BAB, 0x1BAC, 0x1BAD, 0x1BE6, 0x1BE8, 0x1BE9, 0x1BED, 0x1BEF,
0x1BF0, 0x1BF1, 0x1C2C, 0x1C2D, 0x1C2E, 0x1C2F, 0x1C30, 0x1C31,
0x1C32, 0x1C33, 0x1C36, 0x1C37, 0x1CD0, 0x1CD1, 0x1CD2, 0x1CD4,
0x1CD5, 0x1CD6, 0x1CD7, 0x1CD8, 0x1CD9, 0x1CDA, 0x1CDB, 0x1CDC,
0x1CDD, 0x1CDE, 0x1CDF, 0x1CE0, 0x1CE2, 0x1CE3, 0x1CE4, 0x1CE5,
0x1CE6, 0x1CE7, 0x1CE8, 0x1CED, 0x1CF4, 0x1CF8, 0x1CF9, 0x1DC0,
0x1DC1, 0x1DC2, 0x1DC3, 0x1DC4, 0x1DC5, 0x1DC6, 0x1DC7, 0x1DC8,
0x1DC9, 0x1DCA, 0x1DCB, 0x1DCC, 0x1DCD, 0x1DCE, 0x1DCF, 0x1DD0,
0x1DD1, 0x1DD2, 0x1DD3, 0x1DD4, 0x1DD5, 0x1DD6, 0x1DD7, 0x1DD8,
0x1DD9, 0x1DDA, 0x1DDB, 0x1DDC, 0x1DDD, 0x1DDE, 0x1DDF, 0x1DE0,
0x1DE1, 0x1DE2, 0x1DE3, 0x1DE4, 0x1DE5, 0x1DE6, 0x1DE7, 0x1DE8,
0x1DE9, 0x1DEA, 0x1DEB, 0x1DEC, 0x1DED, 0x1DEE, 0x1DEF, 0x1DF0,
0x1DF1, 0x1DF2, 0x1DF3, 0x1DF4, 0x1DF5, 0x1DF6, 0x1DF7, 0x1DF8,
0x1DF9, 0x1DFA, 0x1DFB, 0x1DFC, 0x1DFD, 0x1DFE, 0x1DFF, 0x20D0,
0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7, 0x20D8,
0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20E1, 0x20E5, 0x20E6, 0x20E7,
0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF,
0x20F0, 0x2CEF, 0x2CF0, 0x2CF1, 0x2D7F, 0x2DE0, 0x2DE1, 0x2DE2,
0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6, 0x2DE7, 0x2DE8, 0x2DE9, 0x2DEA,
0x2DEB, 0x2DEC, 0x2DED, 0x2DEE, 0x2DEF, 0x2DF0, 0x2DF1, 0x2DF2,
0x2DF3, 0x2DF4, 0x2DF5, 0x2DF6, 0x2DF7, 0x2DF8, 0x2DF9, 0x2DFA,
0x2DFB, 0x2DFC, 0x2DFD, 0x2DFE, 0x2DFF, 0x302A, 0x302B, 0x302C,
0x302D, 0x3099, 0x309A, 0xA66F, 0xA674, 0xA675, 0xA676, 0xA677,
0xA678, 0xA679, 0xA67A, 0xA67B, 0xA67C, 0xA67D, 0xA69E, 0xA69F,
0xA6F0, 0xA6F1, 0xA802, 0xA806, 0xA80B, 0xA825, 0xA826, 0xA82C,
0xA8C4, 0xA8C5, 0xA8E0, 0xA8E1, 0xA8E2, 0xA8E3, 0xA8E4, 0xA8E5,
0xA8E6, 0xA8E7, 0xA8E8, 0xA8E9, 0xA8EA, 0xA8EB, 0xA8EC, 0xA8ED,
0xA8EE, 0xA8EF, 0xA8F0, 0xA8F1, 0xA8FF, 0xA926, 0xA927, 0xA928,
0xA929, 0xA92A, 0xA92B, 0xA92C, 0xA92D, 0xA947, 0xA948, 0xA949,
0xA94A, 0xA94B, 0xA94C, 0xA94D, 0xA94E, 0xA94F, 0xA950, 0xA951,
0xA980, 0xA981, 0xA982, 0xA9B3, 0xA9B6, 0xA9B7, 0xA9B8, 0xA9B9,
0xA9BC, 0xA9BD, 0xA9E5, 0xAA29, 0xAA2A, 0xAA2B, 0xAA2C, 0xAA2D,
0xAA2E, 0xAA31, 0xAA32, 0xAA35, 0xAA36, 0xAA43, 0xAA4C, 0xAA7C,
0xAAB0, 0xAAB2, 0xAAB3, 0xAAB4, 0xAAB7, 0xAAB8, 0xAABE, 0xAABF,
0xAAC1, 0xAAEC, 0xAAED, 0xAAF6, 0xABE5, 0xABE8, 0xABED, 0xFB1E,
0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07,
0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F,
0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27,
0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F,
0x101FD, 0x102E0, 0x10376, 0x10377, 0x10378, 0x10379, 0x1037A, 0x10A01,
0x10A02, 0x10A03, 0x10A05, 0x10A06, 0x10A0C, 0x10A0D, 0x10A0E, 0x10A0F,
0x10A38, 0x10A39, 0x10A3A, 0x10A3F, 0x10AE5, 0x10AE6, 0x10D24, 0x10D25,
0x10D26, 0x10D27, 0x10EAB, 0x10EAC, 0x10F46, 0x10F47, 0x10F48, 0x10F49,
0x10F4A, 0x10F4B, 0x10F4C, 0x10F4D, 0x10F4E, 0x10F4F, 0x10F50, 0x10F82,
0x10F83, 0x10F84, 0x10F85, 0x11001, 0x11038, 0x11039, 0x1103A, 0x1103B,
0x1103C, 0x1103D, 0x1103E, 0x1103F, 0x11040, 0x11041, 0x11042, 0x11043,
0x11044, 0x11045, 0x11046, 0x11070, 0x11073, 0x11074, 0x1107F, 0x11080,
0x11081, 0x110B3, 0x110B4, 0x110B5, 0x110B6, 0x110B9, 0x110BA, 0x110C2,
0x11100, 0x11101, 0x11102, 0x11127, 0x11128, 0x11129, 0x1112A, 0x1112B,
0x1112D, 0x1112E, 0x1112F, 0x11130, 0x11131, 0x11132, 0x11133, 0x11134,
0x11173, 0x11180, 0x11181, 0x111B6, 0x111B7, 0x111B8, 0x111B9, 0x111BA,
0x111BB, 0x111BC, 0x111BD, 0x111BE, 0x111C9, 0x111CA, 0x111CB, 0x111CC,
0x111CF, 0x1122F, 0x11230, 0x11231, 0x11234, 0x11236, 0x11237, 0x1123E,
0x112DF, 0x112E3, 0x112E4, 0x112E5, 0x112E6, 0x112E7, 0x112E8, 0x112E9,
0x112EA, 0x11300, 0x11301, 0x1133B, 0x1133C, 0x11340, 0x11366, 0x11367,
0x11368, 0x11369, 0x1136A, 0x1136B, 0x1136C, 0x11370, 0x11371, 0x11372,
0x11373, 0x11374, 0x11438, 0x11439, 0x1143A, 0x1143B, 0x1143C, 0x1143D,
0x1143E, 0x1143F, 0x11442, 0x11443, 0x11444, 0x11446, 0x1145E, 0x114B3,
0x114B4, 0x114B5, 0x114B6, 0x114B7, 0x114B8, 0x114BA, 0x114BF, 0x114C0,
0x114C2, 0x114C3, 0x115B2, 0x115B3, 0x115B4, 0x115B5, 0x115BC, 0x115BD,
0x115BF, 0x115C0, 0x115DC, 0x115DD, 0x11633, 0x11634, 0x11635, 0x11636,
0x11637, 0x11638, 0x11639, 0x1163A, 0x1163D, 0x1163F, 0x11640, 0x116AB,
0x116AD, 0x116B0, 0x116B1, 0x116B2, 0x116B3, 0x116B4, 0x116B5, 0x116B7,
0x1171D, 0x1171E, 0x1171F, 0x11722, 0x11723, 0x11724, 0x11725, 0x11727,
0x11728, 0x11729, 0x1172A, 0x1172B, 0x1182F, 0x11830, 0x11831, 0x11832,
0x11833, 0x11834, 0x11835, 0x11836, 0x11837, 0x11839, 0x1183A, 0x1193B,
0x1193C, 0x1193E, 0x11943, 0x119D4, 0x119D5, 0x119D6, 0x119D7, 0x119DA,
0x119DB, 0x119E0, 0x11A01, 0x11A02, 0x11A03, 0x11A04, 0x11A05, 0x11A06,
0x11A07, 0x11A08, 0x11A09, 0x11A0A, 0x11A33, 0x11A34, 0x11A35, 0x11A36,
0x11A37, 0x11A38, 0x11A3B, 0x11A3C, 0x11A3D, 0x11A3E, 0x11A47, 0x11A51,
0x11A52, 0x11A53, 0x11A54, 0x11A55, 0x11A56, 0x11A59, 0x11A5A, 0x11A5B,
0x11A8A, 0x11A8B, 0x11A8C, 0x11A8D, 0x11A8E, 0x11A8F, 0x11A90, 0x11A91,
0x11A92, 0x11A93, 0x11A94, 0x11A95, 0x11A96, 0x11A98, 0x11A99, 0x11C30,
0x11C31, 0x11C32, 0x11C33, 0x11C34, 0x11C35, 0x11C36, 0x11C38, 0x11C39,
0x11C3A, 0x11C3B, 0x11C3C, 0x11C3D, 0x11C3F, 0x11C92, 0x11C93, 0x11C94,
0x11C95, 0x11C96, 0x11C97, 0x11C98, 0x11C99, 0x11C9A, 0x11C9B, 0x11C9C,
0x11C9D, 0x11C9E, 0x11C9F, 0x11CA0, 0x11CA1, 0x11CA2, 0x11CA3, 0x11CA4,
0x11CA5, 0x11CA6, 0x11CA7, 0x11CAA, 0x11CAB, 0x11CAC, 0x11CAD, 0x11CAE,
0x11CAF, 0x11CB0, 0x11CB2, 0x11CB3, 0x11CB5, 0x11CB6, 0x11D31, 0x11D32,
0x11D33, 0x11D34, 0x11D35, 0x11D36, 0x11D3A, 0x11D3C, 0x11D3D, 0x11D3F,
0x11D40, 0x11D41, 0x11D42, 0x11D43, 0x11D44, 0x11D45, 0x11D47, 0x11D90,
0x11D91, 0x11D95, 0x11D97, 0x11EF3, 0x11EF4, 0x16AF0, 0x16AF1, 0x16AF2,
0x16AF3, 0x16AF4, 0x16B30, 0x16B31, 0x16B32, 0x16B33, 0x16B34, 0x16B35,
0x16B36, 0x16F4F, 0x16F8F, 0x16F90, 0x16F91, 0x16F92, 0x16FE4, 0x1BC9D,
0x1BC9E, 0x1CF00, 0x1CF01, 0x1CF02, 0x1CF03, 0x1CF04, 0x1CF05, 0x1CF06,
0x1CF07, 0x1CF08, 0x1CF09, 0x1CF0A, 0x1CF0B, 0x1CF0C, 0x1CF0D, 0x1CF0E,
0x1CF0F, 0x1CF10, 0x1CF11, 0x1CF12, 0x1CF13, 0x1CF14, 0x1CF15, 0x1CF16,
0x1CF17, 0x1CF18, 0x1CF19, 0x1CF1A, 0x1CF1B, 0x1CF1C, 0x1CF1D, 0x1CF1E,
0x1CF1F, 0x1CF20, 0x1CF21, 0x1CF22, 0x1CF23, 0x1CF24, 0x1CF25, 0x1CF26,
0x1CF27, 0x1CF28, 0x1CF29, 0x1CF2A, 0x1CF2B, 0x1CF2C, 0x1CF2D, 0x1CF30,
0x1CF31, 0x1CF32, 0x1CF33, 0x1CF34, 0x1CF35, 0x1CF36, 0x1CF37, 0x1CF38,
0x1CF39, 0x1CF3A, 0x1CF3B, 0x1CF3C, 0x1CF3D, 0x1CF3E, 0x1CF3F, 0x1CF40,
0x1CF41, 0x1CF42, 0x1CF43, 0x1CF44, 0x1CF45, 0x1CF46, 0x1D167, 0x1D168,
0x1D169, 0x1D17B, 0x1D17C, 0x1D17D, 0x1D17E, 0x1D17F, 0x1D180, 0x1D181,
0x1D182, 0x1D185, 0x1D186, 0x1D187, 0x1D188, 0x1D189, 0x1D18A, 0x1D18B,
0x1D1AA, 0x1D1AB, 0x1D1AC, 0x1D1AD, 0x1D242, 0x1D243, 0x1D244, 0x1DA00,
0x1DA01, 0x1DA02, 0x1DA03, 0x1DA04, 0x1DA05, 0x1DA06, 0x1DA07, 0x1DA08,
0x1DA09, 0x1DA0A, 0x1DA0B, 0x1DA0C, 0x1DA0D, 0x1DA0E, 0x1DA0F, 0x1DA10,
0x1DA11, 0x1DA12, 0x1DA13, 0x1DA14, 0x1DA15, 0x1DA16, 0x1DA17, 0x1DA18,
0x1DA19, 0x1DA1A, 0x1DA1B, 0x1DA1C, 0x1DA1D, 0x1DA1E, 0x1DA1F, 0x1DA20,
0x1DA21, 0x1DA22, 0x1DA23, 0x1DA24, 0x1DA25, 0x1DA26, 0x1DA27, 0x1DA28,
0x1DA29, 0x1DA2A, 0x1DA2B, 0x1DA2C, 0x1DA2D, 0x1DA2E, 0x1DA2F, 0x1DA30,
0x1DA31, 0x1DA32, 0x1DA33, 0x1DA34, 0x1DA35, 0x1DA36, 0x1DA3B, 0x1DA3C,
0x1DA3D, 0x1DA3E, 0x1DA3F, 0x1DA40, 0x1DA41, 0x1DA42, 0x1DA43, 0x1DA44,
0x1DA45, 0x1DA46, 0x1DA47, 0x1DA48, 0x1DA49, 0x1DA4A, 0x1DA4B, 0x1DA4C,
0x1DA4D, 0x1DA4E, 0x1DA4F, 0x1DA50, 0x1DA51, 0x1DA52, 0x1DA53, 0x1DA54,
0x1DA55, 0x1DA56, 0x1DA57, 0x1DA58, 0x1DA59, 0x1DA5A, 0x1DA5B, 0x1DA5C,
0x1DA5D, 0x1DA5E, 0x1DA5F, 0x1DA60, 0x1DA61, 0x1DA62, 0x1DA63, 0x1DA64,
0x1DA65, 0x1DA66, 0x1DA67, 0x1DA68, 0x1DA69, 0x1DA6A, 0x1DA6B, 0x1DA6C,
0x1DA75, 0x1DA84, 0x1DA9B, 0x1DA9C, 0x1DA9D, 0x1DA9E, 0x1DA9F, 0x1DAA1,
0x1DAA2, 0x1DAA3, 0x1DAA4, 0x1DAA5, 0x1DAA6, 0x1DAA7, 0x1DAA8, 0x1DAA9,
0x1DAAA, 0x1DAAB, 0x1DAAC, 0x1DAAD, 0x1DAAE, 0x1DAAF, 0x1E000, 0x1E001,
0x1E002, 0x1E003, 0x1E004, 0x1E005, 0x1E006, 0x1E008, 0x1E009, 0x1E00A,
0x1E00B, 0x1E00C, 0x1E00D, 0x1E00E, 0x1E00F, 0x1E010, 0x1E011, 0x1E012,
0x1E013, 0x1E014, 0x1E015, 0x1E016, 0x1E017, 0x1E018, 0x1E01B, 0x1E01C,
0x1E01D, 0x1E01E, 0x1E01F, 0x1E020, 0x1E021, 0x1E023, 0x1E024, 0x1E026,
0x1E027, 0x1E028, 0x1E029, 0x1E02A, 0x1E130, 0x1E131, 0x1E132, 0x1E133,
0x1E134, 0x1E135, 0x1E136, 0x1E2AE, 0x1E2EC, 0x1E2ED, 0x1E2EE, 0x1E2EF,
0x1E8D0, 0x1E8D1, 0x1E8D2, 0x1E8D3, 0x1E8D4, 0x1E8D5, 0x1E8D6, 0x1E944,
0x1E945, 0x1E946, 0x1E947, 0x1E948, 0x1E949, 0x1E94A, 0xE0100, 0xE0101,
0xE0102, 0xE0103, 0xE0104, 0xE0105, 0xE0106, 0xE0107, 0xE0108, 0xE0109,
0xE010A, 0xE010B, 0xE010C, 0xE010D, 0xE010E, 0xE010F, 0xE0110, 0xE0111,
0xE0112, 0xE0113, 0xE0114, 0xE0115, 0xE0116, 0xE0117, 0xE0118, 0xE0119,
0xE011A, 0xE011B, 0xE011C, 0xE011D, 0xE011E, 0xE011F, 0xE0120, 0xE0121,
0xE0122, 0xE0123, 0xE0124, 0xE0125, 0xE0126, 0xE0127, 0xE0128, 0xE0129,
0xE012A, 0xE012B, 0xE012C, 0xE012D, 0xE012E, 0xE012F, 0xE0130, 0xE0131,
0xE0132, 0xE0133, 0xE0134, 0xE0135, 0xE0136, 0xE0137, 0xE0138, 0xE0139,
0xE013A, 0xE013B, 0xE013C, 0xE013D, 0xE013E, 0xE013F, 0xE0140, 0xE0141,
0xE0142, 0xE0143, 0xE0144, 0xE0145, 0xE0146, 0xE0147, 0xE0148, 0xE0149,
0xE014A, 0xE014B, 0xE014C, 0xE014D, 0xE014E, 0xE014F, 0xE0150, 0xE0151,
0xE0152, 0xE0153, 0xE0154, 0xE0155, 0xE0156, 0xE0157, 0xE0158, 0xE0159,
0xE015A, 0xE015B, 0xE015C, 0xE015D, 0xE015E, 0xE015F, 0xE0160, 0xE0161,
0xE0162, 0xE0163, 0xE0164, 0xE0165, 0xE0166, 0xE0167, 0xE0168, 0xE0169,
0xE016A, 0xE016B, 0xE016C, 0xE016D, 0xE016E, 0xE016F, 0xE0170, 0xE0171,
0xE0172, 0xE0173, 0xE0174, 0xE0175, 0xE0176, 0xE0177, 0xE0178, 0xE0179,
0xE017A, 0xE017B, 0xE017C, 0xE017D, 0xE017E, 0xE017F, 0xE0180, 0xE0181,
0xE0182, 0xE0183, 0xE0184, 0xE0185, 0xE0186, 0xE0187, 0xE0188, 0xE0189,
0xE018A, 0xE018B, 0xE018C, 0xE018D, 0xE018E, 0xE018F, 0xE0190, 0xE0191,
0xE0192, 0xE0193, 0xE0194, 0xE0195, 0xE0196, 0xE0197, 0xE0198, 0xE0199,
0xE019A, 0xE019B, 0xE019C, 0xE019D, 0xE019E, 0xE019F, 0xE01A0, 0xE01A1,
0xE01A2, 0xE01A3, 0xE01A4, 0xE01A5, 0xE01A6, 0xE01A7, 0xE01A8, 0xE01A9,
0xE01AA, 0xE01AB, 0xE01AC, 0xE01AD, 0xE01AE, 0xE01AF, 0xE01B0, 0xE01B1,
0xE01B2, 0xE01B3, 0xE01B4, 0xE01B5, 0xE01B6, 0xE01B7, 0xE01B8, 0xE01B9,
0xE01BA, 0xE01BB, 0xE01BC, 0xE01BD, 0xE01BE, 0xE01BF, 0xE01C0, 0xE01C1,
0xE01C2, 0xE01C3, 0xE01C4, 0xE01C5, 0xE01C6, 0xE01C7, 0xE01C8, 0xE01C9,
0xE01CA, 0xE01CB, 0xE01CC, 0xE01CD, 0xE01CE, 0xE01CF, 0xE01D0, 0xE01D1,
0xE01D2, 0xE01D3, 0xE01D4, 0xE01D5, 0xE01D6, 0xE01D7, 0xE01D8, 0xE01D9,
0xE01DA, 0xE01DB, 0xE01DC, 0xE01DD, 0xE01DE, 0xE01DF, 0xE01E0, 0xE01E1,
0xE01E2, 0xE01E3, 0xE01E4, 0xE01E5, 0xE01E6, 0xE01E7, 0xE01E8, 0xE01E9,
0xE01EA, 0xE01EB, 0xE01EC, 0xE01ED, 0xE01EE, 0xE01EF,
};
static unsigned long combiningCharTableSize = sizeof(combiningCharTable) / sizeof(combiningCharTable[0]);
/* Check if the code is a wide character
*/
static int isWideChar(unsigned long cp) {
size_t i;
for (i = 0; i < wideCharTableSize; i++)
if (wideCharTable[i][0] <= cp && cp <= wideCharTable[i][1]) return 1;
return 0;
}
/* Check if the code is a combining character
*/
static int isCombiningChar(unsigned long cp) {
size_t i;
for (i = 0; i < combiningCharTableSize; i++)
if (combiningCharTable[i] == cp) return 1;
return 0;
}
/* Get length of previous UTF8 character
*/
static size_t prevUtf8CharLen(const char* buf, int pos) {
int end = pos--;
while (pos >= 0 && ((unsigned char)buf[pos] & 0xC0) == 0x80)
pos--;
return end - pos;
}
/* Convert UTF8 to Unicode code point
*/
static size_t utf8BytesToCodePoint(const char* buf, size_t len, int* cp) {
if (len) {
unsigned char byte = buf[0];
if ((byte & 0x80) == 0) {
*cp = byte;
return 1;
} else if ((byte & 0xE0) == 0xC0) {
if (len >= 2) {
*cp = (((unsigned long)(buf[0] & 0x1F)) << 6) |
((unsigned long)(buf[1] & 0x3F));
return 2;
}
} else if ((byte & 0xF0) == 0xE0) {
if (len >= 3) {
*cp = (((unsigned long)(buf[0] & 0x0F)) << 12) |
(((unsigned long)(buf[1] & 0x3F)) << 6) |
((unsigned long)(buf[2] & 0x3F));
return 3;
}
} else if ((byte & 0xF8) == 0xF0) {
if (len >= 4) {
*cp = (((unsigned long)(buf[0] & 0x07)) << 18) |
(((unsigned long)(buf[1] & 0x3F)) << 12) |
(((unsigned long)(buf[2] & 0x3F)) << 6) |
((unsigned long)(buf[3] & 0x3F));
return 4;
}
}
}
return 0;
}
/* Get length of next grapheme
*/
size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len) {
size_t beg = pos;
int cp;
size_t len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp);
if (isCombiningChar(cp)) {
/* NOTREACHED */
return 0;
}
if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1;
pos += len;
while (pos < buf_len) {
len = utf8BytesToCodePoint(buf + pos, buf_len - pos, &cp);
if (!isCombiningChar(cp)) return pos - beg;
pos += len;
}
return pos - beg;
}
/* Get length of previous grapheme
*/
size_t linenoiseUtf8PrevCharLen(const char* buf, size_t UNUSED(buf_len), size_t pos, size_t *col_len) {
size_t end = pos;
while (pos > 0) {
size_t len = prevUtf8CharLen(buf, pos);
pos -= len;
int cp;
utf8BytesToCodePoint(buf + pos, len, &cp);
if (!isCombiningChar(cp)) {
if (col_len != NULL) *col_len = isWideChar(cp) ? 2 : 1;
return end - pos;
}
}
/* NOTREACHED */
return 0;
}
/* Read a Unicode from file.
*/
size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp) {
if (buf_len < 1) return -1;
size_t nread = read(fd,&buf[0],1);
if (nread <= 0) return nread;
unsigned char byte = buf[0];
if ((byte & 0x80) == 0) {
;
} else if ((byte & 0xE0) == 0xC0) {
if (buf_len < 2) return -1;
nread = read(fd,&buf[1],1);
if (nread <= 0) return nread;
} else if ((byte & 0xF0) == 0xE0) {
if (buf_len < 3) return -1;
nread = read(fd,&buf[1],2);
if (nread <= 0) return nread;
} else if ((byte & 0xF8) == 0xF0) {
if (buf_len < 3) return -1;
nread = read(fd,&buf[1],3);
if (nread <= 0) return nread;
} else {
return -1;
}
return utf8BytesToCodePoint(buf, buf_len, cp);
}

View file

@ -0,0 +1,55 @@
/* encodings/utf8.h -- VERSION 1.0
*
* Guerrilla line editing library against the idea that a line editing lib
* needs to be 20,000 lines of C code.
*
* See linenoise.c for more information.
*
* ------------------------------------------------------------------------
*
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __LINENOISE_ENCODINGS_UTF8_H
#define __LINENOISE_ENCODINGS_UTF8_H
#ifdef __cplusplus
extern "C" {
#endif
size_t linenoiseUtf8PrevCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len);
size_t linenoiseUtf8NextCharLen(const char* buf, size_t buf_len, size_t pos, size_t *col_len);
size_t linenoiseUtf8ReadCode(int fd, char* buf, size_t buf_len, int* cp);
#ifdef __cplusplus
}
#endif
#endif /* __LINENOISE_ENCODINGS_UTF8_H */

View file

@ -28,6 +28,7 @@
#include "completion.h"
#include "configuration.h"
#include "linenoise/linenoise.h"
#include "linenoise/utf8.h"
#include "yl_opt.h"
int done;
@ -53,6 +54,8 @@ main(int argc, char *argv[])
/* continue in interactive mode */
linenoiseSetCompletionCallback(complete_cmd);
linenoiseSetEncodingFunctions(linenoiseUtf8PrevCharLen, linenoiseUtf8NextCharLen, linenoiseUtf8ReadCode);
linenoiseSetMultiLine(1);
load_config();
if (ly_ctx_new(NULL, YL_DEFAULT_CTX_OPTIONS, &ctx)) {