<commit_before>/**
 * @file parser.c
 * @author <NAME> <<EMAIL>>
 * @brief common libyang parsers routines implementations
 *
 * Copyright (c) 2015 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */

#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pcre.h>

#include "common.h"
#include "context.h"
#include "libyang.h"
#include "parser.h"
#include "resolve.h"
#include "tree_internal.h"
#include "parser_yang.h"

#define LYP_URANGE_LEN 19

static char *lyp_ublock2urange[][2] = {
    {"BasicLatin", "[\\x{0000}-\\x{007F}]"},
    {"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"},
    {"LatinExtended-A", "[\\x{0100}-\\x{017F}]"},
    {"LatinExtended-B", "[\\x{0180}-\\x{024F}]"},
    {"IPAExtensions", "[\\x{0250}-\\x{02AF}]"},
    {"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"},
    {"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"},
    {"Greek", "[\\x{0370}-\\x{03FF}]"},
    {"Cyrillic", "[\\x{0400}-\\x{04FF}]"},
    {"Armenian", "[\\x{0530}-\\x{058F}]"},
    {"Hebrew", "[\\x{0590}-\\x{05FF}]"},
    {"Arabic", "[\\x{0600}-\\x{06FF}]"},
    {"Syriac", "[\\x{0700}-\\x{074F}]"},
    {"Thaana", "[\\x{0780}-\\x{07BF}]"},
    {"Devanagari", "[\\x{0900}-\\x{097F}]"},
    {"Bengali", "[\\x{0980}-\\x{09FF}]"},
    {"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"},
    {"Gujarati", "[\\x{0A80}-\\x{0AFF}]"},
    {"Oriya", "[\\x{0B00}-\\x{0B7F}]"},
    {"Tamil", "[\\x{0B80}-\\x{0BFF}]"},
    {"Telugu", "[\\x{0C00}-\\x{0C7F}]"},
    {"Kannada", "[\\x{0C80}-\\x{0CFF}]"},
    {"Malayalam", "[\\x{0D00}-\\x{0D7F}]"},
    {"Sinhala", "[\\x{0D80}-\\x{0DFF}]"},
    {"Thai", "[\\x{0E00}-\\x{0E7F}]"},
    {"Lao", "[\\x{0E80}-\\x{0EFF}]"},
    {"Tibetan", "[\\x{0F00}-\\x{0FFF}]"},
    {"Myanmar", "[\\x{1000}-\\x{109F}]"},
    {"Georgian", "[\\x{10A0}-\\x{10FF}]"},
    {"HangulJamo", "[\\x{1100}-\\x{11FF}]"},
    {"Ethiopic", "[\\x{1200}-\\x{137F}]"},
    {"Cherokee", "[\\x{13A0}-\\x{13FF}]"},
    {"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"},
    {"Ogham", "[\\x{1680}-\\x{169F}]"},
    {"Runic", "[\\x{16A0}-\\x{16FF}]"},
    {"Khmer", "[\\x{1780}-\\x{17FF}]"},
    {"Mongolian", "[\\x{1800}-\\x{18AF}]"},
    {"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"},
    {"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"},
    {"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"},
    {"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"},
    {"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"},
    {"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"},
    {"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"},
    {"NumberForms", "[\\x{2150}-\\x{218F}]"},
    {"Arrows", "[\\x{2190}-\\x{21FF}]"},
    {"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"},
    {"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"},
    {"ControlPictures", "[\\x{2400}-\\x{243F}]"},
    {"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"},
    {"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"},
    {"BoxDrawing", "[\\x{2500}-\\x{257F}]"},
    {"BlockElements", "[\\x{2580}-\\x{259F}]"},
    {"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"},
    {"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"},
    {"Dingbats", "[\\x{2700}-\\x{27BF}]"},
    {"BraillePatterns", "[\\x{2800}-\\x{28FF}]"},
    {"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"},
    {"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"},
    {"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"},
    {"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"},
    {"Hiragana", "[\\x{3040}-\\x{309F}]"},
    {"Katakana", "[\\x{30A0}-\\x{30FF}]"},
    {"Bopomofo", "[\\x{3100}-\\x{312F}]"},
    {"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"},
    {"Kanbun", "[\\x{3190}-\\x{319F}]"},
    {"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"},
    {"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"},
    {"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"},
    {"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"},
    {"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"},
    {"YiSyllables", "[\\x{A000}-\\x{A48F}]"},
    {"YiRadicals", "[\\x{A490}-\\x{A4CF}]"},
    {"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"},
    {"PrivateUse", "[\\x{E000}-\\x{F8FF}]"},
    {"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"},
    {"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"},
    {"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"},
    {"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"},
    {"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"},
    {"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"},
    {"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"},
    {"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"},
    {NULL, NULL}
};

int
lyp_is_rpc_action(struct lys_node *node)
{
    assert(node);

    while (lys_parent(node)) {
        node = lys_parent(node);
        if (node->nodetype == LYS_ACTION) {
            break;
        }
    }

    if (node->nodetype & (LYS_RPC | LYS_ACTION)) {
        return 1;
    } else {
        return 0;
    }
}

int
lyp_check_options(int options)
{
    int x = options & LYD_OPT_TYPEMASK;

    /* LYD_OPT_NOAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG */
    if (options & LYD_OPT_NOAUTODEL) {
        if (x != LYD_OPT_DATA && x != LYD_OPT_CONFIG) {
            return 1;
        }
    }

    /* "is power of 2" algorithm, with 0 exception */
    return x ? !(x && !(x & (x - 1))) : 0;
}

/**
 * @brief Alternative for lys_read() + lys_parse() in case of import
 *
 * @param[in] fd MUST be a regular file (will be used by mmap)
 */
struct lys_module *
lys_read_import(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, const char *revision, int implement)
{
    struct lys_module *module = NULL;
    struct stat sb;
    char *addr;

    if (!ctx || fd < 0) {
        LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
        return NULL;
    }


    if (fstat(fd, &sb) == -1) {
        LOGERR(LY_ESYS, "Failed to stat the file descriptor (%s).", strerror(errno));
        return NULL;
    }

    if (!sb.st_size) {
        LOGERR(LY_EINVAL, "File empty.");
        return NULL;
    }

    addr = mmap(NULL, sb.st_size + 2, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    if (addr == MAP_FAILED) {
        LOGERR(LY_EMEM,"Map file into memory failed (%s()).",__func__);
        return NULL;
    }
    switch (format) {
    case LYS_IN_YIN:
        module = yin_read_module(ctx, addr, revision, implement);
        break;
    case LYS_IN_YANG:
        module = yang_read_module(ctx, addr, sb.st_size + 2, revision, implement);
        break;
    default:
        LOGERR(LY_EINVAL, "%s: Invalid format parameter.", __func__);
        break;
    }
    munmap(addr, sb.st_size + 2);

    return module;
}

/* if module is !NULL, then the function searches for submodule */
struct lys_module *
lyp_search_file(struct ly_ctx *ctx, struct lys_module *module, const char *name, const char *revision,
                int implement, struct unres_schema *unres)
{
    size_t len, flen, match_len = 0, dir_len;
    int fd, i;
    char *wd, *wn = NULL;
    DIR *dir = NULL;
    struct dirent *file;
    char *match_name = NULL, *dot, *rev, *filename;
    LYS_INFORMAT format, match_format = 0;
    struct lys_module *result = NULL;
    unsigned int u;
    struct ly_set *dirs;
    struct stat st;

    /* start to fill the dir fifo with the context's search path (if set)
     * and the current working directory */
    dirs = ly_set_new();
    if (!dirs) {
        LOGMEM;
        return NULL;
    }

    len = strlen(name);
    wd = get_current_dir_name();
    if (!wd) {
        LOGMEM;
        goto cleanup;
    } else if (ly_set_add(dirs, wd, 0) == -1) {
        goto cleanup;
    }
    if (ctx->models.search_path) {
        wd = strdup(ctx->models.search_path);
        if (!wd) {
            LOGMEM;
            goto cleanup;
        } else if (ly_set_add(dirs, wd, 0) == -1) {
            goto cleanup;
        }
    }
    wd = NULL;

    /* start searching */
    while (dirs->number) {
        free(wd);
        free(wn); wn = NULL;

        dirs->number--;
        wd = (char *)dirs->set.g[dirs->number];
        dirs->set.g[dirs->number] = NULL;
        LOGVRB("Searching for \"%s\" in %s.", name, wd);

        if (dir) {
            closedir(dir);
        }
        dir = opendir(wd);
        dir_len = strlen(wd);
        if (!dir) {
            LOGWRN("Unable to open directory \"%s\" for searching (sub)modules (%s).", wd, strerror(errno));
        } else {
            while ((file = readdir(dir))) {
                if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
                    /* skip . and .. */
                    continue;
                }
                free(wn);
                if (asprintf(&wn, "%s/%s", wd, file->d_name) == -1) {
                    LOGMEM;
                    goto cleanup;
                }
                if (stat(wn, &st) == -1) {
                    LOGWRN("Unable to get information about \"%s\" file in \"%s\" when searching for (sub)modules (%s)",
                           file->d_name, wd, strerror(errno));
                    continue;
                }
                if (S_ISDIR(st.st_mode) && dirs->number) {
                    /* we have another subdirectory in searchpath to explore,
                     * subdirectories are not taken into account in current working dir (dirs->set.g[0]) */
                    if (ly_set_add(dirs, wn, 0) == -1) {
                        goto cleanup;
                    }
                    /* continue with the next item in current directory */
                    wn = NULL;
                    continue;
                } else if (!S_ISREG(st.st_mode)) {
                    /* not a regular file (note that we see the target of symlinks instead of symlinks */
                    continue;
                }

                /* here we know that the item is a file which can contain a module */
                if (strncmp(name, file->d_name, len) ||
                        (file->d_name[len] != '.' && file->d_name[len] != '@')) {
                    /* different filename than the module we search for */
                    continue;
                }

                /* get type according to filename suffix */
                flen = strlen(file->d_name);
                if (!strcmp(&file->d_name[flen - 4], ".yin")) {
                    format = LYS_IN_YIN;
                } else if (!strcmp(&file->d_name[flen - 5], ".yang")) {
                    format = LYS_IN_YANG;
                } else {
                    /* not supportde suffix/file format */
                    continue;
                }

                if (revision) {
                    /* we look for the specific revision, try to get it from the filename */
                    if (file->d_name[len] == '@') {
                        /* check revision from the filename */
                        if (strncmp(revision, &file->d_name[len + 1], strlen(revision))) {
                            /* another revision */
                            continue;
                        } else {
                            /* exact revision */
                            free(match_name);
                            match_name = wn;
                            wn = NULL;
                            match_len = dir_len + 1 + len;
                            match_format = format;
                            goto matched;
                        }
                    } else {
                        /* continue trying to find exact revision match, use this only if not found */
                        free(match_name);
                        match_name = wn;
                        wn = NULL;
                        match_len = dir_len + 1 +len;
                        match_format = format;
                        continue;
                    }
                } else {
                    /* remember the revision and try to find the newest one */
                    if (match_name) {
                        if (file->d_name[len] != '@' || lyp_check_date(&file->d_name[len + 1])) {
                            continue;
                        } else if (match_name[match_len] == '@' &&
                                (strncmp(&match_name[match_len + 1], &file->d_name[len + 1], LY_REV_SIZE - 1) >= 0)) {
                            continue;
                        }
                        free(match_name);
                    }

                    match_name = wn;
                    wn = NULL;
                    match_len = dir_len + 1 + len;
                    match_format = format;
                    continue;
                }
            }
        }
    }

    if (!match_name) {
        if (!module && !revision) {
            /* otherwise the module would be already taken from the context */
            result = (struct lys_module *)ly_ctx_get_module(ctx, name, revision);
        }
        if (!result) {
            LOGERR(LY_ESYS, "Data model \"%s\" not found.", name, ctx->models.search_path, wd);
        }
        goto cleanup;
    }

matched:
    LOGVRB("Loading schema from \"%s\" file.", match_name);

    /* cut the format for now */
    dot = strrchr(match_name, '.');
    dot[1] = '\0';

    /* check that the same file was not already loaded - it make sense only in case of loading the newest revision,
     * search also in disabled module - if the matching module is disabled, it will be enabled instead of loading it */
    if (!revision) {
        for (i = 0; i < ctx->models.used; ++i) {
            if (ctx->models.list[i]->filepath && !strcmp(name, ctx->models.list[i]->name)
                    && !strncmp(match_name, ctx->models.list[i]->filepath, strlen(match_name))) {
                result = ctx->models.list[i];
                if (implement && !result->implemented) {
                    /* make it implemented now */
                    if (lys_set_implemented(result)) {
                        result = NULL;
                    }
                }
                if (result->disabled) {
                    lys_set_enabled(result);
                }

                goto cleanup;
            }
        }
    }

    /* add the format back */
    dot[1] = 'y';

    /* open the file */
    fd = open(match_name, O_RDONLY);
    if (fd < 0) {
        LOGERR(LY_ESYS, "Unable to open data model file \"%s\" (%s).",
               match_name, strerror(errno));
        goto cleanup;
    }

    if (module) {
        result = (struct lys_module *)lys_submodule_read(module, fd, match_format, unres);
    } else {
        result = lys_read_import(ctx, fd, match_format, revision, implement);
    }
    close(fd);

    if (!result) {
        goto cleanup;
    }

    /* check that name and revision match filename */
    filename = strrchr(match_name, '/');
    if (!filename) {
        filename = match_name;
    } else {
        filename++;
    }
    rev = strchr(filename, '@');
    /* name */
    len = strlen(result->name);
    if (strncmp(filename, result->name, len) ||
            ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
        LOGWRN("File name \"%s\" does not match module name \"%s\".", filename, result->name);
    }
    if (rev) {
        len = dot - ++rev;
        if (!result->rev_size || len != 10 || strncmp(result->rev[0].date, rev, len)) {
            LOGWRN("File name \"%s\" does not match module revision \"%s\".", filename,
                   result->rev_size ? result->rev[0].date : "none");
        }
    }

    result->filepath = lydict_insert_zc(ctx, match_name);
    match_name = NULL;
    /* success */

cleanup:
    free(wn);
    free(wd);
    if (dir) {
        closedir(dir);
    }
    free(match_name);
    for (u = 0; u < dirs->number; u++) {
        free(dirs->set.g[u]);
    }
    ly_set_free(dirs);

    return result;
}

/* logs directly
 * base: 0  - to accept decimal, octal, hexadecimal (in default value)
 *       10 - to accept only decimal (instance value)
 */
static int
parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret, struct lyd_node *node)
{
    char *strptr;

    if (!val_str || !val_str[0]) {
        if (node) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, "", node->schema->name);
        } else {
            ly_errno = LY_EVALID;
            ly_vecode = LYVE_INVAL;
        }
        return EXIT_FAILURE;
    }

    /* convert to 64-bit integer, all the redundant characters are handled */
    errno = 0;
    strptr = NULL;

    /* parse the value */
    *ret = strtoll(val_str, &strptr, base);
    if (errno || (*ret < min) || (*ret > max)) {
        if (node) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, val_str, node->schema->name);
        } else {
            ly_errno = LY_EVALID;
            ly_vecode = LYVE_INVAL;
        }
        return EXIT_FAILURE;
    } else if (strptr && *strptr) {
        while (isspace(*strptr)) {
            ++strptr;
        }
        if (*strptr) {
            if (node) {
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, val_str, node->schema->name);
            } else {
                ly_errno = LY_EVALID;
                ly_vecode = LYVE_INVAL;
            }
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}

/* logs directly
 * base: 0  - to accept decimal, octal, hexadecimal (in default value)
 *       10 - to accept only decimal (instance value)
 */
static int
parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret, struct lyd_node *node)
{
    char *strptr;

    if (!val_str || !val_str[0]) {
        if (node) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, "", node->schema->name);
        } else {
            ly_errno = LY_EVALID;
            ly_vecode = LYVE_INVAL;
        }
        return EXIT_FAILURE;
    }

    errno = 0;
    strptr = NULL;
    *ret = strtoull(val_str, &strptr, base);
    if (errno || (*ret > max)) {
        if (node) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, val_str, node->schema->name);
        } else {
            ly_errno = LY_EVALID;
            ly_vecode = LYVE_INVAL;
        }
        return EXIT_FAILURE;
    } else if (strptr && *strptr) {
        while (isspace(*strptr)) {
            ++strptr;
        }
        if (*strptr) {
            if (node) {
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, val_str, node->schema->name);
            } else {
                ly_errno = LY_EVALID;
                ly_vecode = LYVE_INVAL;
            }
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}

/* logs directly
 *
 * kind == 0 - unsigned (unum used), 1 - signed (snum used), 2 - floating point (fnum used)
 */
static int
validate_length_range(uint8_t kind, uint64_t unum, int64_t snum, int64_t fnum, uint8_t fnum_dig, struct lys_type *type,
                      const char *val_str, struct lyd_node *node)
{
    struct lys_restr *restr = NULL;
    struct len_ran_intv *intv = NULL, *tmp_intv;
    struct lys_type *cur_type;
    int match;

    if (resolve_len_ran_interval(NULL, type, &intv)) {
        /* already done during schema parsing */
        LOGINT;
        return EXIT_FAILURE;
    }
    if (!intv) {
        return EXIT_SUCCESS;
    }

    /* I know that all intervals belonging to a single restriction share one type pointer */
    tmp_intv = intv;
    cur_type = intv->type;
    do {
        match = 0;
        for (; tmp_intv && (tmp_intv->type == cur_type); tmp_intv = tmp_intv->next) {
            if (match) {
                /* just iterate through the rest of this restriction intervals */
                continue;
            }

            if (((kind == 0) && (unum < tmp_intv->value.uval.min))
                    || ((kind == 1) && (snum < tmp_intv->value.sval.min))
                    || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) < 0))) {
                break;
            }

            if (((kind == 0) && (unum >= tmp_intv->value.uval.min) && (unum <= tmp_intv->value.uval.max))
                    || ((kind == 1) && (snum >= tmp_intv->value.sval.min) && (snum <= tmp_intv->value.sval.max))
                    || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) > -1)
                    && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.max, cur_type->info.dec64.dig) < 1))) {
                match = 1;
            }
        }

        if (!match) {
            break;
        } else if (tmp_intv) {
            cur_type = tmp_intv->type;
        }
    } while (tmp_intv);

    while (intv) {
        tmp_intv = intv->next;
        free(intv);
        intv = tmp_intv;
    }

    if (!match) {
        switch (cur_type->base) {
        case LY_TYPE_BINARY:
            restr = cur_type->info.binary.length;
            break;
        case LY_TYPE_DEC64:
            restr = cur_type->info.dec64.range;
            break;
        case LY_TYPE_INT8:
        case LY_TYPE_INT16:
        case LY_TYPE_INT32:
        case LY_TYPE_INT64:
        case LY_TYPE_UINT8:
        case LY_TYPE_UINT16:
        case LY_TYPE_UINT32:
        case LY_TYPE_UINT64:
            restr = cur_type->info.num.range;
            break;
        case LY_TYPE_STRING:
            restr = cur_type->info.str.length;
            break;
        default:
            LOGINT;
            return EXIT_FAILURE;
        }

        LOGVAL(LYE_NOCONSTR, LY_VLOG_LYD, node, (val_str ? val_str : ""), restr ? restr->expr : "");
        if (restr && restr->emsg) {
            LOGVAL(LYE_SPEC, LY_VLOG_LYD, node, restr->emsg);
        }
        if (restr && restr->eapptag) {
            strncpy(((struct ly_err *)&ly_errno)->apptag, restr->eapptag, LY_APPTAG_LEN - 1);
        }
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

/* logs directly */
static int
validate_pattern(const char *val_str, struct lys_type *type, struct lyd_node *node)
{
    int i, rc;
    pcre *precomp;

    assert(type->base == LY_TYPE_STRING);

    if (!val_str) {
        val_str = "";
    }

    if (type->der && validate_pattern(val_str, &type->der->type, node)) {
        return EXIT_FAILURE;
    }

    for (i = 0; i < type->info.str.pat_count; ++i) {
        if (lyp_check_pattern(&type->info.str.patterns[i].expr[1], &precomp)) {
            LOGINT;
            return EXIT_FAILURE;
        }

        rc = pcre_exec(precomp, NULL, val_str, strlen(val_str), 0, 0, NULL, 0);
        if ((rc && type->info.str.patterns[i].expr[0] == 0x06) || (!rc && type->info.str.patterns[i].expr[0] == 0x15)) {
            LOGVAL(LYE_NOCONSTR, LY_VLOG_LYD, node, val_str, &type->info.str.patterns[i].expr[1]);
            if (type->info.str.patterns[i].emsg) {
                LOGVAL(LYE_SPEC, LY_VLOG_LYD, node, type->info.str.patterns[i].emsg);
            }
            if (type->info.str.patterns[i].eapptag) {
                strncpy(((struct ly_err *)&ly_errno)->apptag, type->info.str.patterns[i].eapptag, LY_APPTAG_LEN - 1);
            }
            free(precomp);
            return EXIT_FAILURE;
        }
        free(precomp);
    }

    return EXIT_SUCCESS;
}

static void
check_number(const char *str_num, const char **num_end, LY_DATA_TYPE base)
{
    if (!isdigit(str_num[0]) && (str_num[0] != '-') && (str_num[0] != '+')) {
        *num_end = str_num;
        return;
    }

    if ((str_num[0] == '-') || (str_num[0] == '+')) {
        ++str_num;
    }

    while (isdigit(str_num[0])) {
        ++str_num;
    }

    if ((base != LY_TYPE_DEC64) || (str_num[0] != '.') || !isdigit(str_num[1])) {
        *num_end = str_num;
        return;
    }

    ++str_num;
    while (isdigit(str_num[0])) {
        ++str_num;
    }

    *num_end = str_num;
}

/**
 * @brief Checks the syntax of length or range statement,
 *        on success checks the semantics as well. Does not log.
 *
 * @param[in] expr Length or range expression.
 * @param[in] type Type with the restriction.
 *
 * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
 */
int
lyp_check_length_range(const char *expr, struct lys_type *type)
{
    struct len_ran_intv *intv = NULL, *tmp_intv;
    const char *c = expr, *tail;
    int ret = EXIT_FAILURE, flg = 1; /* first run flag */

    assert(expr);

lengthpart:

    while (isspace(*c)) {
        c++;
    }

    /* lower boundary or explicit number */
    if (!strncmp(c, "max", 3)) {
max:
        c += 3;
        while (isspace(*c)) {
            c++;
        }
        if (*c != '\0') {
            goto error;
        }

        goto syntax_ok;

    } else if (!strncmp(c, "min", 3)) {
        if (!flg) {
            /* min cannot be used elsewhere than in the first length-part */
            goto error;
        } else {
            flg = 0;
        }
        c += 3;
        while (isspace(*c)) {
            c++;
        }

        if (*c == '|') {
            c++;
            /* process next length-parth */
            goto lengthpart;
        } else if (*c == '\0') {
            goto syntax_ok;
        } else if (!strncmp(c, "..", 2)) {
upper:
            c += 2;
            while (isspace(*c)) {
                c++;
            }
            if (*c == '\0') {
                goto error;
            }

            /* upper boundary */
            if (!strncmp(c, "max", 3)) {
                goto max;
            }

            check_number(c, &tail, type->base);
            if (c == tail) {
                goto error;
            }
            c = tail;
            while (isspace(*c)) {
                c++;
            }
            if (*c == '\0') {
                goto syntax_ok;
            } else if (*c == '|') {
                c++;
                /* process next length-parth */
                goto lengthpart;
            } else {
                goto error;
            }
        } else {
            goto error;
        }

    } else if (isdigit(*c) || (*c == '-') || (*c == '+')) {
        /* number */
        check_number(c, &tail, type->base);
        if (c == tail) {
            goto error;
        }
        c = tail;

        while (isspace(*c)) {
            c++;
        }

        if (*c == '|') {
            c++;
            /* process next length-part */
            goto lengthpart;
        } else if (*c == '\0') {
            goto syntax_ok;
        } else if (!strncmp(c, "..", 2)) {
            goto upper;
        }

    } else {
        goto error;
    }

syntax_ok:
    if (resolve_len_ran_interval(expr, type, &intv)) {
        goto error;
    }

    ret = EXIT_SUCCESS;

error:
    while (intv) {
        tmp_intv = intv->next;
        free(intv);
        intv = tmp_intv;
    }

    return ret;
}

/**
 * @brief Checks pattern syntax. Logs directly.
 *
 * @param[in] pattern Pattern to check.
 * @param[out] pcre_precomp Precompiled PCRE pattern. Can be NULL.
 * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
 */
int
lyp_check_pattern(const char *pattern, pcre **pcre_precomp)
{
    int idx, start, end, err_offset;
    char *perl_regex, *ptr;
    const char *err_msg;
    pcre *precomp;

    /*
     * adjust the expression to a Perl equivalent
     *
     * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs
     */
    perl_regex = malloc((strlen(pattern) + 2) * sizeof(char));
    if (!perl_regex) {
        LOGMEM;
        return EXIT_FAILURE;
    }
    strcpy(perl_regex, pattern);
    if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
        strcat(perl_regex, "$");
    }

    /* substitute Unicode Character Blocks with exact Character Ranges */
    while ((ptr = strstr(perl_regex, "\\p{Is"))) {
        start = ptr - perl_regex;

        ptr = strchr(ptr, '}');
        if (!ptr) {
            LOGVAL(LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 2, "unterminated character property");
            free(perl_regex);
            return EXIT_FAILURE;
        }

        end = (ptr - perl_regex) + 1;

        /* need more space */
        if (end - start < LYP_URANGE_LEN) {
            perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (LYP_URANGE_LEN - (end - start)) + 1);
            if (!perl_regex) {
                LOGMEM;
                free(perl_regex);
                return EXIT_FAILURE;
            }
        }

        /* find our range */
        for (idx = 0; lyp_ublock2urange[idx][0]; ++idx) {
            if (!strncmp(perl_regex + start + 5, lyp_ublock2urange[idx][0], strlen(lyp_ublock2urange[idx][0]))) {
                break;
            }
        }
        if (!lyp_ublock2urange[idx][0]) {
            LOGVAL(LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 5, "unknown block name");
            free(perl_regex);
            return EXIT_FAILURE;
        }

        /* make the space in the string and replace the block */
        memmove(perl_regex + start + LYP_URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1);
        memcpy(perl_regex + start, lyp_ublock2urange[idx][1], LYP_URANGE_LEN);
    }

    /* must return 0, already checked during parsing */
    precomp = pcre_compile(perl_regex, PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE,
                           &err_msg, &err_offset, NULL);
    free(perl_regex);
    if (!precomp) {
        LOGVAL(LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, pattern + err_offset, err_msg);
        return EXIT_FAILURE;
    }

    if (pcre_precomp) {
        *pcre_precomp = precomp;
    } else {
        free(precomp);
    }

    return EXIT_SUCCESS;
}

/**
 * @brief Change the value into its canonical form. In libyang, additionally to the RFC,
 * all identities have their module as a prefix in their canonical form.
 *
 * @param[in] ctx
 * @param[in] type Type of the value.
 * @param[in,out] value Original and then canonical value.
 * @param[in] data1 If \p type is #LY_TYPE_BITS: (struct lys_type_bit **) type bit field,
 *                                #LY_TYPE_DEC64: (int64_t *) parsed digits of the number itself without floating point,
 *                                #LY_TYPE_IDENT: (const char *) local module name (identityref node module),
 *                                #LY_TYPE_INT*: (int64_t *) parsed int number itself,
 *                                #LY_TYPE_UINT*: (uint64_t *) parsed uint number itself,
 *                                otherwise ignored.
 * @param[in] data2 If \p type is #LY_TYPE_BITS: (int *) type bit field length,
 *                                #LY_TYPE_DEC64: (uint8_t *) number of fraction digits (position of the floating point),
 *                                otherwise ignored.
 * @return 1 if a conversion took place, 0 if the value was kept the same.
 */
static int
make_canonical(struct ly_ctx *ctx, int type, const char **value, void *data1, void *data2)
{
    char *buf = ly_buf(), *buf_backup = NULL, *str;
    struct lys_type_bit **bits = NULL;
    const char *module_name;
    int i, j, count, ret = 0;
    int64_t num;
    uint64_t unum;
    uint8_t c;

    /* prepare buffer for creating canonical representation */
    if (ly_buf_used && buf[0]) {
        buf_backup = strndup(buf, LY_BUF_SIZE - 1);
    }
    ly_buf_used++;

    switch (type) {
    case LY_TYPE_BITS:
        bits = (struct lys_type_bit **)data1;
        count = *((int *)data2);
        /* in canonical form, the bits are ordered by their position */
        buf[0] = '\0';
        for (i = 0; i < count; i++) {
            if (!bits[i]) {
                /* bit not set */
                continue;
            }
            if (buf[0]) {
                str = strdup(buf);
                sprintf(buf, "%s %s", str, bits[i]->name);
                free(str);
            } else {
                sprintf(buf, "%s", bits[i]->name);
            }
        }
        break;

    case LY_TYPE_IDENT:
        module_name = (const char *)data1;
        /* identity must always have a prefix */
        if (!strchr(*value, ':')) {
            sprintf(buf, "%s:%s", module_name, *value);
        } else {
            strcpy(buf, *value);
        }
        break;

    case LY_TYPE_DEC64:
        num = *((int64_t *)data1);
        c = *((uint8_t *)data2);
        if (num) {
            count = sprintf(buf, "%"PRId64" ", num);
            if ((count - 1) <= c) {
                /* we have 0. value, add a space for the leading zero */
                count = sprintf(buf, "0%"PRId64" ", num);
            }
            for (i = c, j = 1; i > 0 ; i--) {
                if (j && i > 1 && buf[count - 2] == '0') {
                    /* we have trailing zero to skip */
                    buf[count - 1] = '\0';
                } else {
                    j = 0;
                    buf[count - 1] = buf[count - 2];
                }
                count--;
            }
            buf[count - 1] = '.';
        } else {
            /* zero */
            sprintf(buf, "0.0");
        }
        break;

    case LY_TYPE_INT8:
    case LY_TYPE_INT16:
    case LY_TYPE_INT32:
    case LY_TYPE_INT64:
        num = *((int64_t *)data1);
        sprintf(buf, "%"PRId64, num);
        break;

    case LY_TYPE_UINT8:
    case LY_TYPE_UINT16:
    case LY_TYPE_UINT32:
    case LY_TYPE_UINT64:
        unum = *((uint64_t *)data1);
        sprintf(buf, "%"PRIu64, unum);
        break;

    default:
        /* should not be even called - just do nothing */
        goto cleanup;
    }

    if (strcmp(buf, *value)) {
        lydict_remove(ctx, *value);
        *value = lydict_insert(ctx, buf, 0);
        ret = 1;
    }

cleanup:
    if (buf_backup) {
        /* return previous internal buffer content */
        strcpy(buf, buf_backup);
        free(buf_backup);
    }
    ly_buf_used--;

    return ret;
}

static const char *
ident_val_add_module_prefix(const char *value, const struct lyxml_elem *xml, struct ly_ctx *ctx)
{
    const struct lyxml_ns *ns;
    const struct lys_module *mod;
    char *str;

    do {
        LY_TREE_FOR((struct lyxml_ns *)xml->attr, ns) {
            if ((ns->type == LYXML_ATTR_NS) && !ns->prefix) {
                /* match */
                break;
            }
        }
        if (!ns) {
            xml = xml->parent;
        }
    } while (!ns && xml);

    if (!ns) {
        /* no default namespace */
        LOGINT;
        return NULL;
    }

    /* find module */
    mod = ly_ctx_get_module_by_ns(ctx, ns->value, NULL);
    if (!mod) {
        LOGINT;
        return NULL;
    }

    if (asprintf(&str, "%s:%s", mod->name, value) == -1) {
        LOGMEM;
        return NULL;
    }
    lydict_remove(ctx, value);

    return lydict_insert_zc(ctx, str);
}

/*
 * xml  - optional for converting instance-identifier and identityref into JSON format
 * leaf - mandatory to know the context (necessary e.g. for prefixes in idenitytref values)
 * store - flag for union resolution - we do not want to store the result, we are just learning the type
 */
struct lys_type *
lyp_parse_value(struct lys_type *type, const char **value_, struct lyxml_elem *xml, struct lyd_node_leaf_list *leaf,
                int store, int dflt)
{
    struct lys_type *ret = NULL, *t;
    int c, i, j, len, found = 0, hidden;
    int64_t num;
    uint64_t unum;
    const char *ptr, *ptr2, *value = *value_;
    struct lys_type_bit **bits = NULL;
    struct lys_ident *ident;

    assert(leaf);

    switch (type->base) {
    case LY_TYPE_BINARY:
        /* get number of octets for length validation */
        unum = 0;
        if (value) {
            ptr = value;
            ptr2 = strchr(value, '\n');
            while (ptr2) {
                unum += ptr2 - ptr;
                ptr = ptr2 + 1;
                ptr2 = strchr(ptr, '\n');
            }
            unum += strlen(ptr);
        }

        if (unum & 3) {
            /* base64 length must be multiple of 4 chars */
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
            LOGVAL(LYE_SPEC, LY_VLOG_LYD, leaf, "Base64 encoded value length must be divisible by 4.");
            goto cleanup;
        }
        len = (unum / 4) * 3;
        /* check padding */
        if (unum) {
            if (ptr[strlen(ptr) - 1] == '=') {
                len--;
            }
            if (ptr[strlen(ptr) - 2] == '=') {
                len--;
            }
        }
        if (validate_length_range(0, len, 0, 0, 0, type, value, (struct lyd_node *)leaf)) {
            goto cleanup;
        }

        if (store) {
            /* store the result */
            leaf->value.binary = value;
            leaf->value_type = LY_TYPE_BINARY;
        }
        break;

    case LY_TYPE_BITS:
        /* locate bits structure with the bits definitions
         * since YANG 1.1 allows restricted bits, it is the first
         * bits type with some explicit bit specification */
        for (; !type->info.bits.count; type = &type->der->type);

        if (value || store) {
            /* allocate the array of pointers to bits definition */
            bits = calloc(type->info.bits.count, sizeof *bits);
            if (!bits) {
                LOGMEM;
                goto cleanup;
            }
        }

        if (!value) {
            /* no bits set */
            if (store) {
                /* store empty array */
                leaf->value.bit = bits;
                leaf->value_type = LY_TYPE_BITS;
            }
            break;
        }

        c = 0;
        i = 0;
        while (value[c]) {
            /* skip leading whitespaces */
            while (isspace(value[c])) {
                c++;
            }

            /* get the length of the bit identifier */
            for (len = 0; value[c] && !isspace(value[c]); c++, len++);

            /* go back to the beginning of the identifier */
            c = c - len;

            /* find bit definition, identifiers appear ordered by their posititon */
            for (found = i = 0; i < type->info.bits.count; i++) {
                if (!strncmp(type->info.bits.bit[i].name, &value[c], len) && !type->info.bits.bit[i].name[len]) {
                    /* we have match, check if the value is enabled ... */
                    for (j = 0; j < type->info.bits.bit[i].iffeature_size; j++) {
                        if (!resolve_iffeature(&type->info.bits.bit[i].iffeature[i])) {
                            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
                            LOGVAL(LYE_SPEC, LY_VLOG_LYD, leaf,
                                   "Bit \"%s\" is disabled by its if-feature condition.", type->info.bits.bit[i].name);

                            free(bits);
                            goto cleanup;
                        }
                    }
                    /* check that the value was not already set */
                    if (bits[i]) {
                        LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
                        LOGVAL(LYE_SPEC, LY_VLOG_LYD, leaf, "Bit \"%s\" used multiple times.",
                               type->info.bits.bit[i].name);
                        free(bits);
                        goto cleanup;
                    }
                    /* ... and then store the pointer */
                    bits[i] = &type->info.bits.bit[i];

                    /* stop searching */
                    found = 1;
                    break;
                }
            }

            if (!found) {
                /* referenced bit value does not exists */
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
                free(bits);
                goto cleanup;
            }
            c = c + len;
        }

        make_canonical(type->parent->module->ctx, LY_TYPE_BITS, value_, bits, &type->info.bits.count);

        if (store) {
            /* store the result */
            leaf->value.bit = bits;
            leaf->value_type = LY_TYPE_BITS;
        } else {
            free(bits);
        }
        break;

    case LY_TYPE_BOOL:
        if (value && !strcmp(value, "true")) {
            if (store) {
                leaf->value.bln = 1;
            }
        } else if (!value || strcmp(value, "false")) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value ? value : "", leaf->schema->name);
            goto cleanup;
        }
        /* else stays 0 */

        if (store) {
            leaf->value_type = LY_TYPE_BOOL;
        }
        break;

    case LY_TYPE_DEC64:
        if (!value || !value[0]) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, "", leaf->schema->name);
            goto cleanup;
        }

        ptr = value;
        if (parse_range_dec64(&ptr, type->info.dec64.dig, &num) || ptr[0]) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
            goto cleanup;
        }

        if (validate_length_range(2, 0, 0, num, type->info.dec64.dig, type, value, (struct lyd_node *)leaf)) {
            goto cleanup;
        }

        make_canonical(type->parent->module->ctx, LY_TYPE_DEC64, value_, &num, &type->info.dec64.dig);

        if (store) {
            /* store the result */
            leaf->value.dec64 = num;
            leaf->value_type = LY_TYPE_DEC64;
        }
        break;

    case LY_TYPE_EMPTY:
        if (value && value[0]) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
            goto cleanup;
        }

        if (store) {
            leaf->value_type = LY_TYPE_EMPTY;
        }
        break;

    case LY_TYPE_ENUM:
        /* locate enums structure with the enumeration definitions,
         * since YANG 1.1 allows restricted enums, it is the first
         * enum type with some explicit enum specification */
        for (; !type->info.enums.count; type = &type->der->type);

        /* find matching enumeration value */
        for (i = found = 0; i < type->info.enums.count; i++) {
            if (value && !strcmp(value, type->info.enums.enm[i].name)) {
                /* we have match, check if the value is enabled ... */
                for (j = 0; j < type->info.enums.enm[i].iffeature_size; j++) {
                    if (!resolve_iffeature(&type->info.enums.enm[i].iffeature[i])) {
                        LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
                        LOGVAL(LYE_SPEC, LY_VLOG_LYD, leaf, "Enum \"%s\" is disabled by its if-feature condition.",
                               value);
                        goto cleanup;
                    }
                }
                /* ... and store pointer to the definition */
                if (store) {
                    leaf->value.enm = &type->info.enums.enm[i];
                    leaf->value_type = LY_TYPE_ENUM;
                }
                found = 1;
                break;
            }
        }

        if (!found) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value ? value : "", leaf->schema->name);
            goto cleanup;
        }
        break;

    case LY_TYPE_IDENT:
        if (!value) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, "", leaf->schema->name);
            goto cleanup;
        }

        if (xml) {
            /* first, convert value into the json format */
            value = transform_xml2json(type->parent->module->ctx, value, xml, 0);
            if (!value) {
                /* invalid identityref format */
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, *value_, leaf->schema->name);
                goto cleanup;
            }

            /* the value has no prefix (default namespace), but the element's namespace has a prefix, find default namespace */
            if (!strchr(value, ':') && xml->ns->prefix) {
                value = ident_val_add_module_prefix(value, xml, type->parent->module->ctx);
                if (!value) {
                    goto cleanup;
                }
            }
        } else if (dflt) {
            /* turn logging off */
            hidden = *ly_vlog_hide_location();
            ly_vlog_hide(1);

            /* the value actually uses module's prefixes instead of the module names as in JSON format,
             * we have to convert it */
            value = transform_schema2json(leaf->schema->module, value);
            if (!value) {
                /* invalid identityref format or it was already transformed, so ignore the error here */
                value = lydict_insert(type->parent->module->ctx, *value_, 0);
                /* erase error information */
                ly_err_clean(1);
            }
            /* turn logging back on */
            if (!hidden) {
                ly_vlog_hide(0);
            }
        } else {
            value = lydict_insert(type->parent->module->ctx, *value_, 0);
        }
        /* value is now in the dictionary, whether it differs from *value_ or not */

        /* the value is always changed and includes prefix */
        if (dflt) {
            type->parent->flags |= LYS_DFLTJSON;
        }

        ident = resolve_identref(type, value, (struct lyd_node *)leaf);
        if (!ident) {
            goto cleanup;
        } else if (store) {
            /* store the result */
            leaf->value.ident = ident;
            leaf->value_type = LY_TYPE_IDENT;
        }

        make_canonical(type->parent->module->ctx, LY_TYPE_IDENT, &value,
                           (void *)lyd_node_module((struct lyd_node *)leaf)->name, NULL);

        /* replace the old value with the new one (even if they may be the same) */
        lydict_remove(type->parent->module->ctx, *value_);
        *value_ = value;
        break;

    case LY_TYPE_INST:
        if (!value) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, "", leaf->schema->name);
            goto cleanup;
        }

        if (xml) {
            /* first, convert value into the json format */
            value = transform_xml2json(type->parent->module->ctx, value, xml, 0);
            if (!value) {
                /* invalid instance-identifier format */
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, *value_, leaf->schema->name);
                goto cleanup;
            }
        } else if (dflt) {
            /* turn logging off */
            hidden = *ly_vlog_hide_location();
            ly_vlog_hide(1);

            /* the value actually uses module's prefixes instead of the module names as in JSON format,
             * we have to convert it */
            value = transform_schema2json(leaf->schema->module, value);
            if (!value) {
                /* invalid identityref format or it was already transformed, so ignore the error here */
                value = *value_;
                /* erase error information */
                ly_err_clean(1);
            } else if (value == *value_) {
                /* we have actually created the same expression (prefixes are the same as the module names)
                 * so we have just increased dictionary's refcount - fix it */
                lydict_remove(type->parent->module->ctx, value);
            }
            /* turn logging back on */
            if (!hidden) {
                ly_vlog_hide(0);
            }
        }

        if (store) {
            /* note that the data node is an unresolved instance-identifier */
            leaf->value.instance = NULL;
            leaf->value_type = LY_TYPE_INST | LY_TYPE_INST_UNRES;
        }

        if (value != *value_) {
            /* update the changed value */
            lydict_remove(type->parent->module->ctx, *value_);
            *value_ = value;

            /* we have to remember the conversion into JSON format to be able to print it in correct form */
            if (dflt) {
                type->parent->flags |= LYS_DFLTJSON;
            }
        }
        break;

    case LY_TYPE_LEAFREF:
        if (!value) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, "", leaf->schema->name);
            goto cleanup;
        }

        /* it is called not only to get the final type, but mainly to update value to canonical or JSON form
         * if needed */
        t = lyp_parse_value(&type->info.lref.target->type, value_, xml, leaf, 0, dflt);
        value = *value_; /* refresh possibly changed value */
        if (!t) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
            goto cleanup;
        }

        if (store) {
            /* make the note that the data node is an unresolved leafref (value union was already filled) */
            leaf->value_type = t->base | LY_TYPE_LEAFREF_UNRES;
        }

        type = t;
        break;

    case LY_TYPE_STRING:
        if (validate_length_range(0, (value ? strlen(value) : 0), 0, 0, 0, type, value, (struct lyd_node *)leaf)) {
            goto cleanup;
        }

        if (validate_pattern(value, type, (struct lyd_node *)leaf)) {
            goto cleanup;
        }

        if (store) {
            /* store the result */
            leaf->value.string = value;
            leaf->value_type = LY_TYPE_STRING;
        }
        break;

    case LY_TYPE_INT8:<commit_msg>parser BUGFIX storing leafref unresolved values<commit_after>/**
 * @file parser.c
 * @author <NAME> <<EMAIL>>
 * @brief common libyang parsers routines implementations
 *
 * Copyright (c) 2015 CESNET, z.s.p.o.
 *
 * This source code is licensed under BSD 3-Clause License (the "License").
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/BSD-3-Clause
 */

#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pcre.h>

#include "common.h"
#include "context.h"
#include "libyang.h"
#include "parser.h"
#include "resolve.h"
#include "tree_internal.h"
#include "parser_yang.h"

#define LYP_URANGE_LEN 19

static char *lyp_ublock2urange[][2] = {
    {"BasicLatin", "[\\x{0000}-\\x{007F}]"},
    {"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"},
    {"LatinExtended-A", "[\\x{0100}-\\x{017F}]"},
    {"LatinExtended-B", "[\\x{0180}-\\x{024F}]"},
    {"IPAExtensions", "[\\x{0250}-\\x{02AF}]"},
    {"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"},
    {"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"},
    {"Greek", "[\\x{0370}-\\x{03FF}]"},
    {"Cyrillic", "[\\x{0400}-\\x{04FF}]"},
    {"Armenian", "[\\x{0530}-\\x{058F}]"},
    {"Hebrew", "[\\x{0590}-\\x{05FF}]"},
    {"Arabic", "[\\x{0600}-\\x{06FF}]"},
    {"Syriac", "[\\x{0700}-\\x{074F}]"},
    {"Thaana", "[\\x{0780}-\\x{07BF}]"},
    {"Devanagari", "[\\x{0900}-\\x{097F}]"},
    {"Bengali", "[\\x{0980}-\\x{09FF}]"},
    {"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"},
    {"Gujarati", "[\\x{0A80}-\\x{0AFF}]"},
    {"Oriya", "[\\x{0B00}-\\x{0B7F}]"},
    {"Tamil", "[\\x{0B80}-\\x{0BFF}]"},
    {"Telugu", "[\\x{0C00}-\\x{0C7F}]"},
    {"Kannada", "[\\x{0C80}-\\x{0CFF}]"},
    {"Malayalam", "[\\x{0D00}-\\x{0D7F}]"},
    {"Sinhala", "[\\x{0D80}-\\x{0DFF}]"},
    {"Thai", "[\\x{0E00}-\\x{0E7F}]"},
    {"Lao", "[\\x{0E80}-\\x{0EFF}]"},
    {"Tibetan", "[\\x{0F00}-\\x{0FFF}]"},
    {"Myanmar", "[\\x{1000}-\\x{109F}]"},
    {"Georgian", "[\\x{10A0}-\\x{10FF}]"},
    {"HangulJamo", "[\\x{1100}-\\x{11FF}]"},
    {"Ethiopic", "[\\x{1200}-\\x{137F}]"},
    {"Cherokee", "[\\x{13A0}-\\x{13FF}]"},
    {"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"},
    {"Ogham", "[\\x{1680}-\\x{169F}]"},
    {"Runic", "[\\x{16A0}-\\x{16FF}]"},
    {"Khmer", "[\\x{1780}-\\x{17FF}]"},
    {"Mongolian", "[\\x{1800}-\\x{18AF}]"},
    {"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"},
    {"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"},
    {"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"},
    {"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"},
    {"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"},
    {"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"},
    {"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"},
    {"NumberForms", "[\\x{2150}-\\x{218F}]"},
    {"Arrows", "[\\x{2190}-\\x{21FF}]"},
    {"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"},
    {"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"},
    {"ControlPictures", "[\\x{2400}-\\x{243F}]"},
    {"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"},
    {"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"},
    {"BoxDrawing", "[\\x{2500}-\\x{257F}]"},
    {"BlockElements", "[\\x{2580}-\\x{259F}]"},
    {"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"},
    {"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"},
    {"Dingbats", "[\\x{2700}-\\x{27BF}]"},
    {"BraillePatterns", "[\\x{2800}-\\x{28FF}]"},
    {"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"},
    {"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"},
    {"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"},
    {"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"},
    {"Hiragana", "[\\x{3040}-\\x{309F}]"},
    {"Katakana", "[\\x{30A0}-\\x{30FF}]"},
    {"Bopomofo", "[\\x{3100}-\\x{312F}]"},
    {"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"},
    {"Kanbun", "[\\x{3190}-\\x{319F}]"},
    {"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"},
    {"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"},
    {"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"},
    {"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"},
    {"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"},
    {"YiSyllables", "[\\x{A000}-\\x{A48F}]"},
    {"YiRadicals", "[\\x{A490}-\\x{A4CF}]"},
    {"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"},
    {"PrivateUse", "[\\x{E000}-\\x{F8FF}]"},
    {"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"},
    {"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"},
    {"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"},
    {"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"},
    {"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"},
    {"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"},
    {"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"},
    {"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"},
    {NULL, NULL}
};

int
lyp_is_rpc_action(struct lys_node *node)
{
    assert(node);

    while (lys_parent(node)) {
        node = lys_parent(node);
        if (node->nodetype == LYS_ACTION) {
            break;
        }
    }

    if (node->nodetype & (LYS_RPC | LYS_ACTION)) {
        return 1;
    } else {
        return 0;
    }
}

int
lyp_check_options(int options)
{
    int x = options & LYD_OPT_TYPEMASK;

    /* LYD_OPT_NOAUTODEL can be used only with LYD_OPT_DATA or LYD_OPT_CONFIG */
    if (options & LYD_OPT_NOAUTODEL) {
        if (x != LYD_OPT_DATA && x != LYD_OPT_CONFIG) {
            return 1;
        }
    }

    /* "is power of 2" algorithm, with 0 exception */
    return x ? !(x && !(x & (x - 1))) : 0;
}

/**
 * @brief Alternative for lys_read() + lys_parse() in case of import
 *
 * @param[in] fd MUST be a regular file (will be used by mmap)
 */
struct lys_module *
lys_read_import(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, const char *revision, int implement)
{
    struct lys_module *module = NULL;
    struct stat sb;
    char *addr;

    if (!ctx || fd < 0) {
        LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
        return NULL;
    }


    if (fstat(fd, &sb) == -1) {
        LOGERR(LY_ESYS, "Failed to stat the file descriptor (%s).", strerror(errno));
        return NULL;
    }

    if (!sb.st_size) {
        LOGERR(LY_EINVAL, "File empty.");
        return NULL;
    }

    addr = mmap(NULL, sb.st_size + 2, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    if (addr == MAP_FAILED) {
        LOGERR(LY_EMEM,"Map file into memory failed (%s()).",__func__);
        return NULL;
    }
    switch (format) {
    case LYS_IN_YIN:
        module = yin_read_module(ctx, addr, revision, implement);
        break;
    case LYS_IN_YANG:
        module = yang_read_module(ctx, addr, sb.st_size + 2, revision, implement);
        break;
    default:
        LOGERR(LY_EINVAL, "%s: Invalid format parameter.", __func__);
        break;
    }
    munmap(addr, sb.st_size + 2);

    return module;
}

/* if module is !NULL, then the function searches for submodule */
struct lys_module *
lyp_search_file(struct ly_ctx *ctx, struct lys_module *module, const char *name, const char *revision,
                int implement, struct unres_schema *unres)
{
    size_t len, flen, match_len = 0, dir_len;
    int fd, i;
    char *wd, *wn = NULL;
    DIR *dir = NULL;
    struct dirent *file;
    char *match_name = NULL, *dot, *rev, *filename;
    LYS_INFORMAT format, match_format = 0;
    struct lys_module *result = NULL;
    unsigned int u;
    struct ly_set *dirs;
    struct stat st;

    /* start to fill the dir fifo with the context's search path (if set)
     * and the current working directory */
    dirs = ly_set_new();
    if (!dirs) {
        LOGMEM;
        return NULL;
    }

    len = strlen(name);
    wd = get_current_dir_name();
    if (!wd) {
        LOGMEM;
        goto cleanup;
    } else if (ly_set_add(dirs, wd, 0) == -1) {
        goto cleanup;
    }
    if (ctx->models.search_path) {
        wd = strdup(ctx->models.search_path);
        if (!wd) {
            LOGMEM;
            goto cleanup;
        } else if (ly_set_add(dirs, wd, 0) == -1) {
            goto cleanup;
        }
    }
    wd = NULL;

    /* start searching */
    while (dirs->number) {
        free(wd);
        free(wn); wn = NULL;

        dirs->number--;
        wd = (char *)dirs->set.g[dirs->number];
        dirs->set.g[dirs->number] = NULL;
        LOGVRB("Searching for \"%s\" in %s.", name, wd);

        if (dir) {
            closedir(dir);
        }
        dir = opendir(wd);
        dir_len = strlen(wd);
        if (!dir) {
            LOGWRN("Unable to open directory \"%s\" for searching (sub)modules (%s).", wd, strerror(errno));
        } else {
            while ((file = readdir(dir))) {
                if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
                    /* skip . and .. */
                    continue;
                }
                free(wn);
                if (asprintf(&wn, "%s/%s", wd, file->d_name) == -1) {
                    LOGMEM;
                    goto cleanup;
                }
                if (stat(wn, &st) == -1) {
                    LOGWRN("Unable to get information about \"%s\" file in \"%s\" when searching for (sub)modules (%s)",
                           file->d_name, wd, strerror(errno));
                    continue;
                }
                if (S_ISDIR(st.st_mode) && dirs->number) {
                    /* we have another subdirectory in searchpath to explore,
                     * subdirectories are not taken into account in current working dir (dirs->set.g[0]) */
                    if (ly_set_add(dirs, wn, 0) == -1) {
                        goto cleanup;
                    }
                    /* continue with the next item in current directory */
                    wn = NULL;
                    continue;
                } else if (!S_ISREG(st.st_mode)) {
                    /* not a regular file (note that we see the target of symlinks instead of symlinks */
                    continue;
                }

                /* here we know that the item is a file which can contain a module */
                if (strncmp(name, file->d_name, len) ||
                        (file->d_name[len] != '.' && file->d_name[len] != '@')) {
                    /* different filename than the module we search for */
                    continue;
                }

                /* get type according to filename suffix */
                flen = strlen(file->d_name);
                if (!strcmp(&file->d_name[flen - 4], ".yin")) {
                    format = LYS_IN_YIN;
                } else if (!strcmp(&file->d_name[flen - 5], ".yang")) {
                    format = LYS_IN_YANG;
                } else {
                    /* not supportde suffix/file format */
                    continue;
                }

                if (revision) {
                    /* we look for the specific revision, try to get it from the filename */
                    if (file->d_name[len] == '@') {
                        /* check revision from the filename */
                        if (strncmp(revision, &file->d_name[len + 1], strlen(revision))) {
                            /* another revision */
                            continue;
                        } else {
                            /* exact revision */
                            free(match_name);
                            match_name = wn;
                            wn = NULL;
                            match_len = dir_len + 1 + len;
                            match_format = format;
                            goto matched;
                        }
                    } else {
                        /* continue trying to find exact revision match, use this only if not found */
                        free(match_name);
                        match_name = wn;
                        wn = NULL;
                        match_len = dir_len + 1 +len;
                        match_format = format;
                        continue;
                    }
                } else {
                    /* remember the revision and try to find the newest one */
                    if (match_name) {
                        if (file->d_name[len] != '@' || lyp_check_date(&file->d_name[len + 1])) {
                            continue;
                        } else if (match_name[match_len] == '@' &&
                                (strncmp(&match_name[match_len + 1], &file->d_name[len + 1], LY_REV_SIZE - 1) >= 0)) {
                            continue;
                        }
                        free(match_name);
                    }

                    match_name = wn;
                    wn = NULL;
                    match_len = dir_len + 1 + len;
                    match_format = format;
                    continue;
                }
            }
        }
    }

    if (!match_name) {
        if (!module && !revision) {
            /* otherwise the module would be already taken from the context */
            result = (struct lys_module *)ly_ctx_get_module(ctx, name, revision);
        }
        if (!result) {
            LOGERR(LY_ESYS, "Data model \"%s\" not found.", name, ctx->models.search_path, wd);
        }
        goto cleanup;
    }

matched:
    LOGVRB("Loading schema from \"%s\" file.", match_name);

    /* cut the format for now */
    dot = strrchr(match_name, '.');
    dot[1] = '\0';

    /* check that the same file was not already loaded - it make sense only in case of loading the newest revision,
     * search also in disabled module - if the matching module is disabled, it will be enabled instead of loading it */
    if (!revision) {
        for (i = 0; i < ctx->models.used; ++i) {
            if (ctx->models.list[i]->filepath && !strcmp(name, ctx->models.list[i]->name)
                    && !strncmp(match_name, ctx->models.list[i]->filepath, strlen(match_name))) {
                result = ctx->models.list[i];
                if (implement && !result->implemented) {
                    /* make it implemented now */
                    if (lys_set_implemented(result)) {
                        result = NULL;
                    }
                }
                if (result->disabled) {
                    lys_set_enabled(result);
                }

                goto cleanup;
            }
        }
    }

    /* add the format back */
    dot[1] = 'y';

    /* open the file */
    fd = open(match_name, O_RDONLY);
    if (fd < 0) {
        LOGERR(LY_ESYS, "Unable to open data model file \"%s\" (%s).",
               match_name, strerror(errno));
        goto cleanup;
    }

    if (module) {
        result = (struct lys_module *)lys_submodule_read(module, fd, match_format, unres);
    } else {
        result = lys_read_import(ctx, fd, match_format, revision, implement);
    }
    close(fd);

    if (!result) {
        goto cleanup;
    }

    /* check that name and revision match filename */
    filename = strrchr(match_name, '/');
    if (!filename) {
        filename = match_name;
    } else {
        filename++;
    }
    rev = strchr(filename, '@');
    /* name */
    len = strlen(result->name);
    if (strncmp(filename, result->name, len) ||
            ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
        LOGWRN("File name \"%s\" does not match module name \"%s\".", filename, result->name);
    }
    if (rev) {
        len = dot - ++rev;
        if (!result->rev_size || len != 10 || strncmp(result->rev[0].date, rev, len)) {
            LOGWRN("File name \"%s\" does not match module revision \"%s\".", filename,
                   result->rev_size ? result->rev[0].date : "none");
        }
    }

    result->filepath = lydict_insert_zc(ctx, match_name);
    match_name = NULL;
    /* success */

cleanup:
    free(wn);
    free(wd);
    if (dir) {
        closedir(dir);
    }
    free(match_name);
    for (u = 0; u < dirs->number; u++) {
        free(dirs->set.g[u]);
    }
    ly_set_free(dirs);

    return result;
}

/* logs directly
 * base: 0  - to accept decimal, octal, hexadecimal (in default value)
 *       10 - to accept only decimal (instance value)
 */
static int
parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret, struct lyd_node *node)
{
    char *strptr;

    if (!val_str || !val_str[0]) {
        if (node) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, "", node->schema->name);
        } else {
            ly_errno = LY_EVALID;
            ly_vecode = LYVE_INVAL;
        }
        return EXIT_FAILURE;
    }

    /* convert to 64-bit integer, all the redundant characters are handled */
    errno = 0;
    strptr = NULL;

    /* parse the value */
    *ret = strtoll(val_str, &strptr, base);
    if (errno || (*ret < min) || (*ret > max)) {
        if (node) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, val_str, node->schema->name);
        } else {
            ly_errno = LY_EVALID;
            ly_vecode = LYVE_INVAL;
        }
        return EXIT_FAILURE;
    } else if (strptr && *strptr) {
        while (isspace(*strptr)) {
            ++strptr;
        }
        if (*strptr) {
            if (node) {
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, val_str, node->schema->name);
            } else {
                ly_errno = LY_EVALID;
                ly_vecode = LYVE_INVAL;
            }
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}

/* logs directly
 * base: 0  - to accept decimal, octal, hexadecimal (in default value)
 *       10 - to accept only decimal (instance value)
 */
static int
parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret, struct lyd_node *node)
{
    char *strptr;

    if (!val_str || !val_str[0]) {
        if (node) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, "", node->schema->name);
        } else {
            ly_errno = LY_EVALID;
            ly_vecode = LYVE_INVAL;
        }
        return EXIT_FAILURE;
    }

    errno = 0;
    strptr = NULL;
    *ret = strtoull(val_str, &strptr, base);
    if (errno || (*ret > max)) {
        if (node) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, val_str, node->schema->name);
        } else {
            ly_errno = LY_EVALID;
            ly_vecode = LYVE_INVAL;
        }
        return EXIT_FAILURE;
    } else if (strptr && *strptr) {
        while (isspace(*strptr)) {
            ++strptr;
        }
        if (*strptr) {
            if (node) {
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, node, val_str, node->schema->name);
            } else {
                ly_errno = LY_EVALID;
                ly_vecode = LYVE_INVAL;
            }
            return EXIT_FAILURE;
        }
    }

    return EXIT_SUCCESS;
}

/* logs directly
 *
 * kind == 0 - unsigned (unum used), 1 - signed (snum used), 2 - floating point (fnum used)
 */
static int
validate_length_range(uint8_t kind, uint64_t unum, int64_t snum, int64_t fnum, uint8_t fnum_dig, struct lys_type *type,
                      const char *val_str, struct lyd_node *node)
{
    struct lys_restr *restr = NULL;
    struct len_ran_intv *intv = NULL, *tmp_intv;
    struct lys_type *cur_type;
    int match;

    if (resolve_len_ran_interval(NULL, type, &intv)) {
        /* already done during schema parsing */
        LOGINT;
        return EXIT_FAILURE;
    }
    if (!intv) {
        return EXIT_SUCCESS;
    }

    /* I know that all intervals belonging to a single restriction share one type pointer */
    tmp_intv = intv;
    cur_type = intv->type;
    do {
        match = 0;
        for (; tmp_intv && (tmp_intv->type == cur_type); tmp_intv = tmp_intv->next) {
            if (match) {
                /* just iterate through the rest of this restriction intervals */
                continue;
            }

            if (((kind == 0) && (unum < tmp_intv->value.uval.min))
                    || ((kind == 1) && (snum < tmp_intv->value.sval.min))
                    || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) < 0))) {
                break;
            }

            if (((kind == 0) && (unum >= tmp_intv->value.uval.min) && (unum <= tmp_intv->value.uval.max))
                    || ((kind == 1) && (snum >= tmp_intv->value.sval.min) && (snum <= tmp_intv->value.sval.max))
                    || ((kind == 2) && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.min, cur_type->info.dec64.dig) > -1)
                    && (dec64cmp(fnum, fnum_dig, tmp_intv->value.fval.max, cur_type->info.dec64.dig) < 1))) {
                match = 1;
            }
        }

        if (!match) {
            break;
        } else if (tmp_intv) {
            cur_type = tmp_intv->type;
        }
    } while (tmp_intv);

    while (intv) {
        tmp_intv = intv->next;
        free(intv);
        intv = tmp_intv;
    }

    if (!match) {
        switch (cur_type->base) {
        case LY_TYPE_BINARY:
            restr = cur_type->info.binary.length;
            break;
        case LY_TYPE_DEC64:
            restr = cur_type->info.dec64.range;
            break;
        case LY_TYPE_INT8:
        case LY_TYPE_INT16:
        case LY_TYPE_INT32:
        case LY_TYPE_INT64:
        case LY_TYPE_UINT8:
        case LY_TYPE_UINT16:
        case LY_TYPE_UINT32:
        case LY_TYPE_UINT64:
            restr = cur_type->info.num.range;
            break;
        case LY_TYPE_STRING:
            restr = cur_type->info.str.length;
            break;
        default:
            LOGINT;
            return EXIT_FAILURE;
        }

        LOGVAL(LYE_NOCONSTR, LY_VLOG_LYD, node, (val_str ? val_str : ""), restr ? restr->expr : "");
        if (restr && restr->emsg) {
            LOGVAL(LYE_SPEC, LY_VLOG_LYD, node, restr->emsg);
        }
        if (restr && restr->eapptag) {
            strncpy(((struct ly_err *)&ly_errno)->apptag, restr->eapptag, LY_APPTAG_LEN - 1);
        }
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

/* logs directly */
static int
validate_pattern(const char *val_str, struct lys_type *type, struct lyd_node *node)
{
    int i, rc;
    pcre *precomp;

    assert(type->base == LY_TYPE_STRING);

    if (!val_str) {
        val_str = "";
    }

    if (type->der && validate_pattern(val_str, &type->der->type, node)) {
        return EXIT_FAILURE;
    }

    for (i = 0; i < type->info.str.pat_count; ++i) {
        if (lyp_check_pattern(&type->info.str.patterns[i].expr[1], &precomp)) {
            LOGINT;
            return EXIT_FAILURE;
        }

        rc = pcre_exec(precomp, NULL, val_str, strlen(val_str), 0, 0, NULL, 0);
        if ((rc && type->info.str.patterns[i].expr[0] == 0x06) || (!rc && type->info.str.patterns[i].expr[0] == 0x15)) {
            LOGVAL(LYE_NOCONSTR, LY_VLOG_LYD, node, val_str, &type->info.str.patterns[i].expr[1]);
            if (type->info.str.patterns[i].emsg) {
                LOGVAL(LYE_SPEC, LY_VLOG_LYD, node, type->info.str.patterns[i].emsg);
            }
            if (type->info.str.patterns[i].eapptag) {
                strncpy(((struct ly_err *)&ly_errno)->apptag, type->info.str.patterns[i].eapptag, LY_APPTAG_LEN - 1);
            }
            free(precomp);
            return EXIT_FAILURE;
        }
        free(precomp);
    }

    return EXIT_SUCCESS;
}

static void
check_number(const char *str_num, const char **num_end, LY_DATA_TYPE base)
{
    if (!isdigit(str_num[0]) && (str_num[0] != '-') && (str_num[0] != '+')) {
        *num_end = str_num;
        return;
    }

    if ((str_num[0] == '-') || (str_num[0] == '+')) {
        ++str_num;
    }

    while (isdigit(str_num[0])) {
        ++str_num;
    }

    if ((base != LY_TYPE_DEC64) || (str_num[0] != '.') || !isdigit(str_num[1])) {
        *num_end = str_num;
        return;
    }

    ++str_num;
    while (isdigit(str_num[0])) {
        ++str_num;
    }

    *num_end = str_num;
}

/**
 * @brief Checks the syntax of length or range statement,
 *        on success checks the semantics as well. Does not log.
 *
 * @param[in] expr Length or range expression.
 * @param[in] type Type with the restriction.
 *
 * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
 */
int
lyp_check_length_range(const char *expr, struct lys_type *type)
{
    struct len_ran_intv *intv = NULL, *tmp_intv;
    const char *c = expr, *tail;
    int ret = EXIT_FAILURE, flg = 1; /* first run flag */

    assert(expr);

lengthpart:

    while (isspace(*c)) {
        c++;
    }

    /* lower boundary or explicit number */
    if (!strncmp(c, "max", 3)) {
max:
        c += 3;
        while (isspace(*c)) {
            c++;
        }
        if (*c != '\0') {
            goto error;
        }

        goto syntax_ok;

    } else if (!strncmp(c, "min", 3)) {
        if (!flg) {
            /* min cannot be used elsewhere than in the first length-part */
            goto error;
        } else {
            flg = 0;
        }
        c += 3;
        while (isspace(*c)) {
            c++;
        }

        if (*c == '|') {
            c++;
            /* process next length-parth */
            goto lengthpart;
        } else if (*c == '\0') {
            goto syntax_ok;
        } else if (!strncmp(c, "..", 2)) {
upper:
            c += 2;
            while (isspace(*c)) {
                c++;
            }
            if (*c == '\0') {
                goto error;
            }

            /* upper boundary */
            if (!strncmp(c, "max", 3)) {
                goto max;
            }

            check_number(c, &tail, type->base);
            if (c == tail) {
                goto error;
            }
            c = tail;
            while (isspace(*c)) {
                c++;
            }
            if (*c == '\0') {
                goto syntax_ok;
            } else if (*c == '|') {
                c++;
                /* process next length-parth */
                goto lengthpart;
            } else {
                goto error;
            }
        } else {
            goto error;
        }

    } else if (isdigit(*c) || (*c == '-') || (*c == '+')) {
        /* number */
        check_number(c, &tail, type->base);
        if (c == tail) {
            goto error;
        }
        c = tail;

        while (isspace(*c)) {
            c++;
        }

        if (*c == '|') {
            c++;
            /* process next length-part */
            goto lengthpart;
        } else if (*c == '\0') {
            goto syntax_ok;
        } else if (!strncmp(c, "..", 2)) {
            goto upper;
        }

    } else {
        goto error;
    }

syntax_ok:
    if (resolve_len_ran_interval(expr, type, &intv)) {
        goto error;
    }

    ret = EXIT_SUCCESS;

error:
    while (intv) {
        tmp_intv = intv->next;
        free(intv);
        intv = tmp_intv;
    }

    return ret;
}

/**
 * @brief Checks pattern syntax. Logs directly.
 *
 * @param[in] pattern Pattern to check.
 * @param[out] pcre_precomp Precompiled PCRE pattern. Can be NULL.
 * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
 */
int
lyp_check_pattern(const char *pattern, pcre **pcre_precomp)
{
    int idx, start, end, err_offset;
    char *perl_regex, *ptr;
    const char *err_msg;
    pcre *precomp;

    /*
     * adjust the expression to a Perl equivalent
     *
     * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs
     */
    perl_regex = malloc((strlen(pattern) + 2) * sizeof(char));
    if (!perl_regex) {
        LOGMEM;
        return EXIT_FAILURE;
    }
    strcpy(perl_regex, pattern);
    if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
        strcat(perl_regex, "$");
    }

    /* substitute Unicode Character Blocks with exact Character Ranges */
    while ((ptr = strstr(perl_regex, "\\p{Is"))) {
        start = ptr - perl_regex;

        ptr = strchr(ptr, '}');
        if (!ptr) {
            LOGVAL(LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 2, "unterminated character property");
            free(perl_regex);
            return EXIT_FAILURE;
        }

        end = (ptr - perl_regex) + 1;

        /* need more space */
        if (end - start < LYP_URANGE_LEN) {
            perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (LYP_URANGE_LEN - (end - start)) + 1);
            if (!perl_regex) {
                LOGMEM;
                free(perl_regex);
                return EXIT_FAILURE;
            }
        }

        /* find our range */
        for (idx = 0; lyp_ublock2urange[idx][0]; ++idx) {
            if (!strncmp(perl_regex + start + 5, lyp_ublock2urange[idx][0], strlen(lyp_ublock2urange[idx][0]))) {
                break;
            }
        }
        if (!lyp_ublock2urange[idx][0]) {
            LOGVAL(LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, perl_regex + start + 5, "unknown block name");
            free(perl_regex);
            return EXIT_FAILURE;
        }

        /* make the space in the string and replace the block */
        memmove(perl_regex + start + LYP_URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1);
        memcpy(perl_regex + start, lyp_ublock2urange[idx][1], LYP_URANGE_LEN);
    }

    /* must return 0, already checked during parsing */
    precomp = pcre_compile(perl_regex, PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE,
                           &err_msg, &err_offset, NULL);
    free(perl_regex);
    if (!precomp) {
        LOGVAL(LYE_INREGEX, LY_VLOG_NONE, NULL, pattern, pattern + err_offset, err_msg);
        return EXIT_FAILURE;
    }

    if (pcre_precomp) {
        *pcre_precomp = precomp;
    } else {
        free(precomp);
    }

    return EXIT_SUCCESS;
}

/**
 * @brief Change the value into its canonical form. In libyang, additionally to the RFC,
 * all identities have their module as a prefix in their canonical form.
 *
 * @param[in] ctx
 * @param[in] type Type of the value.
 * @param[in,out] value Original and then canonical value.
 * @param[in] data1 If \p type is #LY_TYPE_BITS: (struct lys_type_bit **) type bit field,
 *                                #LY_TYPE_DEC64: (int64_t *) parsed digits of the number itself without floating point,
 *                                #LY_TYPE_IDENT: (const char *) local module name (identityref node module),
 *                                #LY_TYPE_INT*: (int64_t *) parsed int number itself,
 *                                #LY_TYPE_UINT*: (uint64_t *) parsed uint number itself,
 *                                otherwise ignored.
 * @param[in] data2 If \p type is #LY_TYPE_BITS: (int *) type bit field length,
 *                                #LY_TYPE_DEC64: (uint8_t *) number of fraction digits (position of the floating point),
 *                                otherwise ignored.
 * @return 1 if a conversion took place, 0 if the value was kept the same.
 */
static int
make_canonical(struct ly_ctx *ctx, int type, const char **value, void *data1, void *data2)
{
    char *buf = ly_buf(), *buf_backup = NULL, *str;
    struct lys_type_bit **bits = NULL;
    const char *module_name;
    int i, j, count, ret = 0;
    int64_t num;
    uint64_t unum;
    uint8_t c;

    /* prepare buffer for creating canonical representation */
    if (ly_buf_used && buf[0]) {
        buf_backup = strndup(buf, LY_BUF_SIZE - 1);
    }
    ly_buf_used++;

    switch (type) {
    case LY_TYPE_BITS:
        bits = (struct lys_type_bit **)data1;
        count = *((int *)data2);
        /* in canonical form, the bits are ordered by their position */
        buf[0] = '\0';
        for (i = 0; i < count; i++) {
            if (!bits[i]) {
                /* bit not set */
                continue;
            }
            if (buf[0]) {
                str = strdup(buf);
                sprintf(buf, "%s %s", str, bits[i]->name);
                free(str);
            } else {
                sprintf(buf, "%s", bits[i]->name);
            }
        }
        break;

    case LY_TYPE_IDENT:
        module_name = (const char *)data1;
        /* identity must always have a prefix */
        if (!strchr(*value, ':')) {
            sprintf(buf, "%s:%s", module_name, *value);
        } else {
            strcpy(buf, *value);
        }
        break;

    case LY_TYPE_DEC64:
        num = *((int64_t *)data1);
        c = *((uint8_t *)data2);
        if (num) {
            count = sprintf(buf, "%"PRId64" ", num);
            if ((count - 1) <= c) {
                /* we have 0. value, add a space for the leading zero */
                count = sprintf(buf, "0%"PRId64" ", num);
            }
            for (i = c, j = 1; i > 0 ; i--) {
                if (j && i > 1 && buf[count - 2] == '0') {
                    /* we have trailing zero to skip */
                    buf[count - 1] = '\0';
                } else {
                    j = 0;
                    buf[count - 1] = buf[count - 2];
                }
                count--;
            }
            buf[count - 1] = '.';
        } else {
            /* zero */
            sprintf(buf, "0.0");
        }
        break;

    case LY_TYPE_INT8:
    case LY_TYPE_INT16:
    case LY_TYPE_INT32:
    case LY_TYPE_INT64:
        num = *((int64_t *)data1);
        sprintf(buf, "%"PRId64, num);
        break;

    case LY_TYPE_UINT8:
    case LY_TYPE_UINT16:
    case LY_TYPE_UINT32:
    case LY_TYPE_UINT64:
        unum = *((uint64_t *)data1);
        sprintf(buf, "%"PRIu64, unum);
        break;

    default:
        /* should not be even called - just do nothing */
        goto cleanup;
    }

    if (strcmp(buf, *value)) {
        lydict_remove(ctx, *value);
        *value = lydict_insert(ctx, buf, 0);
        ret = 1;
    }

cleanup:
    if (buf_backup) {
        /* return previous internal buffer content */
        strcpy(buf, buf_backup);
        free(buf_backup);
    }
    ly_buf_used--;

    return ret;
}

static const char *
ident_val_add_module_prefix(const char *value, const struct lyxml_elem *xml, struct ly_ctx *ctx)
{
    const struct lyxml_ns *ns;
    const struct lys_module *mod;
    char *str;

    do {
        LY_TREE_FOR((struct lyxml_ns *)xml->attr, ns) {
            if ((ns->type == LYXML_ATTR_NS) && !ns->prefix) {
                /* match */
                break;
            }
        }
        if (!ns) {
            xml = xml->parent;
        }
    } while (!ns && xml);

    if (!ns) {
        /* no default namespace */
        LOGINT;
        return NULL;
    }

    /* find module */
    mod = ly_ctx_get_module_by_ns(ctx, ns->value, NULL);
    if (!mod) {
        LOGINT;
        return NULL;
    }

    if (asprintf(&str, "%s:%s", mod->name, value) == -1) {
        LOGMEM;
        return NULL;
    }
    lydict_remove(ctx, value);

    return lydict_insert_zc(ctx, str);
}

/*
 * xml  - optional for converting instance-identifier and identityref into JSON format
 * leaf - mandatory to know the context (necessary e.g. for prefixes in idenitytref values)
 * store - flag for union resolution - we do not want to store the result, we are just learning the type
 */
struct lys_type *
lyp_parse_value(struct lys_type *type, const char **value_, struct lyxml_elem *xml, struct lyd_node_leaf_list *leaf,
                int store, int dflt)
{
    struct lys_type *ret = NULL, *t;
    int c, i, j, len, found = 0, hidden;
    int64_t num;
    uint64_t unum;
    const char *ptr, *ptr2, *value = *value_;
    struct lys_type_bit **bits = NULL;
    struct lys_ident *ident;

    assert(leaf);

    switch (type->base) {
    case LY_TYPE_BINARY:
        /* get number of octets for length validation */
        unum = 0;
        if (value) {
            ptr = value;
            ptr2 = strchr(value, '\n');
            while (ptr2) {
                unum += ptr2 - ptr;
                ptr = ptr2 + 1;
                ptr2 = strchr(ptr, '\n');
            }
            unum += strlen(ptr);
        }

        if (unum & 3) {
            /* base64 length must be multiple of 4 chars */
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
            LOGVAL(LYE_SPEC, LY_VLOG_LYD, leaf, "Base64 encoded value length must be divisible by 4.");
            goto cleanup;
        }
        len = (unum / 4) * 3;
        /* check padding */
        if (unum) {
            if (ptr[strlen(ptr) - 1] == '=') {
                len--;
            }
            if (ptr[strlen(ptr) - 2] == '=') {
                len--;
            }
        }
        if (validate_length_range(0, len, 0, 0, 0, type, value, (struct lyd_node *)leaf)) {
            goto cleanup;
        }

        if (store) {
            /* store the result */
            leaf->value.binary = value;
            leaf->value_type = LY_TYPE_BINARY;
        }
        break;

    case LY_TYPE_BITS:
        /* locate bits structure with the bits definitions
         * since YANG 1.1 allows restricted bits, it is the first
         * bits type with some explicit bit specification */
        for (; !type->info.bits.count; type = &type->der->type);

        if (value || store) {
            /* allocate the array of pointers to bits definition */
            bits = calloc(type->info.bits.count, sizeof *bits);
            if (!bits) {
                LOGMEM;
                goto cleanup;
            }
        }

        if (!value) {
            /* no bits set */
            if (store) {
                /* store empty array */
                leaf->value.bit = bits;
                leaf->value_type = LY_TYPE_BITS;
            }
            break;
        }

        c = 0;
        i = 0;
        while (value[c]) {
            /* skip leading whitespaces */
            while (isspace(value[c])) {
                c++;
            }

            /* get the length of the bit identifier */
            for (len = 0; value[c] && !isspace(value[c]); c++, len++);

            /* go back to the beginning of the identifier */
            c = c - len;

            /* find bit definition, identifiers appear ordered by their posititon */
            for (found = i = 0; i < type->info.bits.count; i++) {
                if (!strncmp(type->info.bits.bit[i].name, &value[c], len) && !type->info.bits.bit[i].name[len]) {
                    /* we have match, check if the value is enabled ... */
                    for (j = 0; j < type->info.bits.bit[i].iffeature_size; j++) {
                        if (!resolve_iffeature(&type->info.bits.bit[i].iffeature[i])) {
                            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
                            LOGVAL(LYE_SPEC, LY_VLOG_LYD, leaf,
                                   "Bit \"%s\" is disabled by its if-feature condition.", type->info.bits.bit[i].name);

                            free(bits);
                            goto cleanup;
                        }
                    }
                    /* check that the value was not already set */
                    if (bits[i]) {
                        LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
                        LOGVAL(LYE_SPEC, LY_VLOG_LYD, leaf, "Bit \"%s\" used multiple times.",
                               type->info.bits.bit[i].name);
                        free(bits);
                        goto cleanup;
                    }
                    /* ... and then store the pointer */
                    bits[i] = &type->info.bits.bit[i];

                    /* stop searching */
                    found = 1;
                    break;
                }
            }

            if (!found) {
                /* referenced bit value does not exists */
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
                free(bits);
                goto cleanup;
            }
            c = c + len;
        }

        make_canonical(type->parent->module->ctx, LY_TYPE_BITS, value_, bits, &type->info.bits.count);

        if (store) {
            /* store the result */
            leaf->value.bit = bits;
            leaf->value_type = LY_TYPE_BITS;
        } else {
            free(bits);
        }
        break;

    case LY_TYPE_BOOL:
        if (value && !strcmp(value, "true")) {
            if (store) {
                leaf->value.bln = 1;
            }
        } else if (!value || strcmp(value, "false")) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value ? value : "", leaf->schema->name);
            goto cleanup;
        }
        /* else stays 0 */

        if (store) {
            leaf->value_type = LY_TYPE_BOOL;
        }
        break;

    case LY_TYPE_DEC64:
        if (!value || !value[0]) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, "", leaf->schema->name);
            goto cleanup;
        }

        ptr = value;
        if (parse_range_dec64(&ptr, type->info.dec64.dig, &num) || ptr[0]) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
            goto cleanup;
        }

        if (validate_length_range(2, 0, 0, num, type->info.dec64.dig, type, value, (struct lyd_node *)leaf)) {
            goto cleanup;
        }

        make_canonical(type->parent->module->ctx, LY_TYPE_DEC64, value_, &num, &type->info.dec64.dig);

        if (store) {
            /* store the result */
            leaf->value.dec64 = num;
            leaf->value_type = LY_TYPE_DEC64;
        }
        break;

    case LY_TYPE_EMPTY:
        if (value && value[0]) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
            goto cleanup;
        }

        if (store) {
            leaf->value_type = LY_TYPE_EMPTY;
        }
        break;

    case LY_TYPE_ENUM:
        /* locate enums structure with the enumeration definitions,
         * since YANG 1.1 allows restricted enums, it is the first
         * enum type with some explicit enum specification */
        for (; !type->info.enums.count; type = &type->der->type);

        /* find matching enumeration value */
        for (i = found = 0; i < type->info.enums.count; i++) {
            if (value && !strcmp(value, type->info.enums.enm[i].name)) {
                /* we have match, check if the value is enabled ... */
                for (j = 0; j < type->info.enums.enm[i].iffeature_size; j++) {
                    if (!resolve_iffeature(&type->info.enums.enm[i].iffeature[i])) {
                        LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
                        LOGVAL(LYE_SPEC, LY_VLOG_LYD, leaf, "Enum \"%s\" is disabled by its if-feature condition.",
                               value);
                        goto cleanup;
                    }
                }
                /* ... and store pointer to the definition */
                if (store) {
                    leaf->value.enm = &type->info.enums.enm[i];
                    leaf->value_type = LY_TYPE_ENUM;
                }
                found = 1;
                break;
            }
        }

        if (!found) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value ? value : "", leaf->schema->name);
            goto cleanup;
        }
        break;

    case LY_TYPE_IDENT:
        if (!value) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, "", leaf->schema->name);
            goto cleanup;
        }

        if (xml) {
            /* first, convert value into the json format */
            value = transform_xml2json(type->parent->module->ctx, value, xml, 0);
            if (!value) {
                /* invalid identityref format */
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, *value_, leaf->schema->name);
                goto cleanup;
            }

            /* the value has no prefix (default namespace), but the element's namespace has a prefix, find default namespace */
            if (!strchr(value, ':') && xml->ns->prefix) {
                value = ident_val_add_module_prefix(value, xml, type->parent->module->ctx);
                if (!value) {
                    goto cleanup;
                }
            }
        } else if (dflt) {
            /* turn logging off */
            hidden = *ly_vlog_hide_location();
            ly_vlog_hide(1);

            /* the value actually uses module's prefixes instead of the module names as in JSON format,
             * we have to convert it */
            value = transform_schema2json(leaf->schema->module, value);
            if (!value) {
                /* invalid identityref format or it was already transformed, so ignore the error here */
                value = lydict_insert(type->parent->module->ctx, *value_, 0);
                /* erase error information */
                ly_err_clean(1);
            }
            /* turn logging back on */
            if (!hidden) {
                ly_vlog_hide(0);
            }
        } else {
            value = lydict_insert(type->parent->module->ctx, *value_, 0);
        }
        /* value is now in the dictionary, whether it differs from *value_ or not */

        /* the value is always changed and includes prefix */
        if (dflt) {
            type->parent->flags |= LYS_DFLTJSON;
        }

        ident = resolve_identref(type, value, (struct lyd_node *)leaf);
        if (!ident) {
            goto cleanup;
        } else if (store) {
            /* store the result */
            leaf->value.ident = ident;
            leaf->value_type = LY_TYPE_IDENT;
        }

        make_canonical(type->parent->module->ctx, LY_TYPE_IDENT, &value,
                           (void *)lyd_node_module((struct lyd_node *)leaf)->name, NULL);

        /* replace the old value with the new one (even if they may be the same) */
        lydict_remove(type->parent->module->ctx, *value_);
        *value_ = value;
        break;

    case LY_TYPE_INST:
        if (!value) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, "", leaf->schema->name);
            goto cleanup;
        }

        if (xml) {
            /* first, convert value into the json format */
            value = transform_xml2json(type->parent->module->ctx, value, xml, 0);
            if (!value) {
                /* invalid instance-identifier format */
                LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, *value_, leaf->schema->name);
                goto cleanup;
            }
        } else if (dflt) {
            /* turn logging off */
            hidden = *ly_vlog_hide_location();
            ly_vlog_hide(1);

            /* the value actually uses module's prefixes instead of the module names as in JSON format,
             * we have to convert it */
            value = transform_schema2json(leaf->schema->module, value);
            if (!value) {
                /* invalid identityref format or it was already transformed, so ignore the error here */
                value = *value_;
                /* erase error information */
                ly_err_clean(1);
            } else if (value == *value_) {
                /* we have actually created the same expression (prefixes are the same as the module names)
                 * so we have just increased dictionary's refcount - fix it */
                lydict_remove(type->parent->module->ctx, value);
            }
            /* turn logging back on */
            if (!hidden) {
                ly_vlog_hide(0);
            }
        }

        if (store) {
            /* note that the data node is an unresolved instance-identifier */
            leaf->value.instance = NULL;
            leaf->value_type = LY_TYPE_INST | LY_TYPE_INST_UNRES;
        }

        if (value != *value_) {
            /* update the changed value */
            lydict_remove(type->parent->module->ctx, *value_);
            *value_ = value;

            /* we have to remember the conversion into JSON format to be able to print it in correct form */
            if (dflt) {
                type->parent->flags |= LYS_DFLTJSON;
            }
        }
        break;

    case LY_TYPE_LEAFREF:
        if (!value) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, "", leaf->schema->name);
            goto cleanup;
        }

        /* it is called not only to get the final type, but mainly to update value to canonical or JSON form
         * if needed */
        t = lyp_parse_value(&type->info.lref.target->type, value_, xml, leaf, store, dflt);
        value = *value_; /* refresh possibly changed value */
        if (!t) {
            LOGVAL(LYE_INVAL, LY_VLOG_LYD, leaf, value, leaf->schema->name);
            goto cleanup;
        }

        if (store) {
            /* make the note that the data node is an unresolved leafref (value union was already filled) */
            leaf->value_type |= LY_TYPE_LEAFREF_UNRES;
        }

        type = t;
        break;

    case LY_TYPE_STRING:
        if (validate_length_range(0, (value ? strlen(value) : 0), 0, 0, 0, type, value, (struct lyd_node *)leaf)) {
            goto cleanup;
        }

        if (validate_pattern(value, type, (struct lyd_node *)leaf)) {
            goto cleanup;
        }

        if (store) {
            /* store the result */
            leaf->value.string = value;
            leaf->value_type = LY_TYPE_STRING;
        }
        break;

    case LY_TYPE_INT8:
{'fraction_non_alphanumeric': 0.14789238667085186, 'fraction_numerical': 0.01675762699851156, 'mean_word_length': 1.6363951843250313, 'pattern_counts': {'":': 0, '<': 75, '<?xml version=': 0, '>': 526, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 42}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3337640', 'n_tokens_mistral': 33378, 'n_tokens_neox': 31490, 'n_words': 7601}