text: /**
   * @file resolve.c
   * @author Michal Vasko <mvasko@cesnet.cz>
   * @brief libyang resolve functions
   *
   * Copyright (c) 2015 - 2018 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 <stdlib.h>
  #include <assert.h>
  #include <string.h>
  #include <ctype.h>
  #include <limits.h>
  
  #include "libyang.h"
  #include "resolve.h"
  #include "common.h"
  #include "xpath.h"
  #include "parser.h"
  #include "parser_yang.h"
  #include "xml_internal.h"
  #include "hash_table.h"
  #include "tree_internal.h"
  #include "extensions.h"
  #include "validation.h"
  
  /* internal parsed predicate structure */
  struct parsed_pred {
      const struct lys_node *schema;
      const char *pred_str;
      int pred_str_len;
  
      int len;
      struct {
          const char *mod_name;
          int mod_name_len;
          const char *name;
          int nam_len;
          const char *value;
          int val_len;
      } *pred;
  };
  
  int
  parse_range_dec64(const char **str_num, uint8_t dig, int64_t *num)
  {
      const char *ptr;
      int minus = 0;
      int64_t ret = 0, prev_ret;
      int8_t str_exp, str_dig = -1, trailing_zeros = 0;
  
      ptr = *str_num;
  
      if (ptr[0] == '-') {
          minus = 1;
          ++ptr;
      } else if (ptr[0] == '+') {
          ++ptr;
      }
  
      if (!isdigit(ptr[0])) {
          /* there must be at least one */
          return 1;
      }
  
      for (str_exp = 0; isdigit(ptr[0]) || ((ptr[0] == '.') && (str_dig < 0)); ++ptr) {
          if (str_exp > 18) {
              return 1;
          }
  
          if (ptr[0] == '.') {
              if (ptr[1] == '.') {
                  /* it's the next interval */
                  break;
              }
              ++str_dig;
          } else {
              prev_ret = ret;
              if (minus) {
                  ret = ret * 10 - (ptr[0] - '0');
                  if (ret > prev_ret) {
                      return 1;
                  }
              } else {
                  ret = ret * 10 + (ptr[0] - '0');
                  if (ret < prev_ret) {
                      return 1;
                  }
              }
              if (str_dig > -1) {
                  ++str_dig;
                  if (ptr[0] == '0') {
                      /* possibly trailing zero */
                      trailing_zeros++;
                  } else {
                      trailing_zeros = 0;
                  }
              }
              ++str_exp;
          }
      }
      if (str_dig == 0) {
          /* no digits after '.' */
          return 1;
      } else if (str_dig == -1) {
          /* there are 0 numbers after the floating point */
          str_dig = 0;
      }
      /* remove trailing zeros */
      if (trailing_zeros) {
          str_dig -= trailing_zeros;
          str_exp -= trailing_zeros;
          ret = ret / dec_pow(trailing_zeros);
      }
  
      /* it's parsed, now adjust the number based on fraction-digits, if needed */
      if (str_dig < dig) {
          if ((str_exp - 1) + (dig - str_dig) > 18) {
              return 1;
          }
          prev_ret = ret;
          ret *= dec_pow(dig - str_dig);
          if ((minus && (ret > prev_ret)) || (!minus && (ret < prev_ret))) {
              return 1;
          }
  
      }
      if (str_dig > dig) {
          return 1;
      }
  
      *str_num = ptr;
      *num = ret;
  
      return 0;
  }
  
  /**
   * @brief Parse an identifier.
   *
   * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
   * identifier          = (ALPHA / "_")
   *                       *(ALPHA / DIGIT / "_" / "-" / ".")
   *
   * @param[in] id Identifier to use.
   *
   * @return Number of characters successfully parsed.
   */
  unsigned int
  parse_identifier(const char *id)
  {
      unsigned int parsed = 0;
  
      assert(id);
  
      if (!isalpha(id[0]) && (id[0] != '_')) {
          return -parsed;
      }
  
      ++parsed;
      ++id;
  
      while (isalnum(id[0]) || (id[0] == '_') || (id[0] == '-') || (id[0] == '.')) {
          ++parsed;
          ++id;
      }
  
      return parsed;
  }
  
  /**
   * @brief Parse a node-identifier.
   *
   * node-identifier     = [module-name ":"] identifier
   *
   * @param[in] id Identifier to use.
   * @param[out] mod_name Points to the module name, NULL if there is not any.
   * @param[out] mod_name_len Length of the module name, 0 if there is not any.
   * @param[out] name Points to the node name.
   * @param[out] nam_len Length of the node name.
   * @param[out] all_desc Whether the path starts with '/', only supported in extended paths.
   * @param[in] extended Whether to accept an extended path (support for [prefix:]*, /[prefix:]*, /[prefix:]., prefix:#identifier).
   *
   * @return Number of characters successfully parsed,
   *         positive on success, negative on failure.
   */
  static int
  parse_node_identifier(const char *id, const char **mod_name, int *mod_name_len, const char **name, int *nam_len,
                        int *all_desc, int extended)
  {
      int parsed = 0, ret, all_desc_local = 0, first_id_len;
      const char *first_id;
  
      assert(id);
      assert((mod_name && mod_name_len) || (!mod_name && !mod_name_len));
      assert((name && nam_len) || (!name && !nam_len));
  
      if (mod_name) {
          *mod_name = NULL;
          *mod_name_len = 0;
      }
      if (name) {
          *name = NULL;
          *nam_len = 0;
      }
  
      if (extended) {
          /* try to parse only the extended expressions */
          if (id[parsed] == '/') {
              if (all_desc) {
                  *all_desc = 1;
              }
              all_desc_local = 1;
          } else {
              if (all_desc) {
                  *all_desc = 0;
              }
          }
  
          /* is there a prefix? */
          ret = parse_identifier(id + all_desc_local);
          if (ret > 0) {
              if (id[all_desc_local + ret] != ':') {
                  /* this is not a prefix, so not an extended id */
                  goto standard_id;
              }
  
              if (mod_name) {
                  *mod_name = id + all_desc_local;
                  *mod_name_len = ret;
              }
  
              /* "/" and ":" */
              ret += all_desc_local + 1;
          } else {
              ret = all_desc_local;
          }
  
          /* parse either "*" or "." */
          if (*(id + ret) == '*') {
              if (name) {
                  *name = id + ret;
                  *nam_len = 1;
              }
              ++ret;
  
              return ret;
          } else if (*(id + ret) == '.') {
              if (!all_desc_local) {
                  /* /. is redundant expression, we do not accept it */
                  return -ret;
              }
  
              if (name) {
                  *name = id + ret;
                  *nam_len = 1;
              }
              ++ret;
  
              return ret;
          } else if (*(id + ret) == '#') {
              if (all_desc_local || !ret) {
                  /* no prefix */
                  return 0;
              }
              parsed = ret + 1;
              if ((ret = parse_identifier(id + parsed)) < 1) {
                  return -parsed + ret;
              }
              *name = id + parsed - 1;
              *nam_len = ret + 1;
              return parsed + ret;
          }
          /* else a standard id, parse it all again */
      }
  
  standard_id:
      if ((ret = parse_identifier(id)) < 1) {
          return ret;
      }
  
      first_id = id;
      first_id_len = ret;
  
      parsed += ret;
      id += ret;
  
      /* there is prefix */
      if (id[0] == ':') {
          ++parsed;
          ++id;
  
      /* there isn't */
      } else {
          if (name) {
              *name = first_id;
              *nam_len = first_id_len;
          }
  
          return parsed;
      }
  
      /* identifier (node name) */
      if ((ret = parse_identifier(id)) < 1) {
          return -parsed + ret;
      }
  
      if (mod_name) {
          *mod_name = first_id;
          *mod_name_len = first_id_len;
      }
      if (name) {
          *name = id;
          *nam_len = ret;
      }
  
      return parsed + ret;
  }
  
  /**
   * @brief Parse a path-predicate (leafref).
   *
   * path-predicate      = "[" *WSP path-equality-expr *WSP "]"
   * path-equality-expr  = node-identifier *WSP "=" *WSP path-key-expr
   *
   * @param[in] id Identifier to use.
   * @param[out] prefix Points to the prefix, NULL if there is not any.
   * @param[out] pref_len Length of the prefix, 0 if there is not any.
   * @param[out] name Points to the node name.
   * @param[out] nam_len Length of the node name.
   * @param[out] path_key_expr Points to the path-key-expr.
   * @param[out] pke_len Length of the path-key-expr.
   * @param[out] has_predicate Flag to mark whether there is another predicate following.
   *
   * @return Number of characters successfully parsed,
   *         positive on success, negative on failure.
   */
  static int
  parse_path_predicate(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len,
                       const char **path_key_expr, int *pke_len, int *has_predicate)
  {
      const char *ptr;
      int parsed = 0, ret;
  
      assert(id);
      if (prefix) {
          *prefix = NULL;
      }
      if (pref_len) {
          *pref_len = 0;
      }
      if (name) {
          *name = NULL;
      }
      if (nam_len) {
          *nam_len = 0;
      }
      if (path_key_expr) {
          *path_key_expr = NULL;
      }
      if (pke_len) {
          *pke_len = 0;
      }
      if (has_predicate) {
          *has_predicate = 0;
      }
  
      if (id[0] != '[') {
          return -parsed;
      }
  
      ++parsed;
      ++id;
  
      while (isspace(id[0])) {
          ++parsed;
          ++id;
      }
  
      if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len, NULL, 0)) < 1) {
          return -parsed + ret;
      }
  
      parsed += ret;
      id += ret;
  
      while (isspace(id[0])) {
          ++parsed;
          ++id;
      }
  
      if (id[0] != '=') {
          return -parsed;
      }
  
      ++parsed;
      ++id;
  
      while (isspace(id[0])) {
          ++parsed;
          ++id;
      }
  
      if ((ptr = strchr(id, ']')) == NULL) {
          return -parsed;
      }
  
      --ptr;
      while (isspace(ptr[0])) {
          --ptr;
      }
      ++ptr;
  
      ret = ptr-id;
      if (path_key_expr) {
          *path_key_expr = id;
      }
      if (pke_len) {
          *pke_len = ret;
      }
  
      parsed += ret;
      id += ret;
  
      while (isspace(id[0])) {
          ++parsed;
          ++id;
      }
  
      assert(id[0] == ']');
  
      if (id[1] == '[') {
          *has_predicate = 1;
      }
  
      return parsed+1;
  }
  
  /**
   * @brief Parse a path-key-expr (leafref). First call parses "current()", all
   *        the ".." and the first node-identifier, other calls parse a single
   *        node-identifier each.
   *
   * path-key-expr       = current-function-invocation *WSP "/" *WSP
   *                       rel-path-keyexpr
   * rel-path-keyexpr    = 1*(".." *WSP "/" *WSP)
   *                       *(node-identifier *WSP "/" *WSP)
   *                       node-identifier
   *
   * @param[in] id Identifier to use.
   * @param[out] prefix Points to the prefix, NULL if there is not any.
   * @param[out] pref_len Length of the prefix, 0 if there is not any.
   * @param[out] name Points to the node name.
   * @param[out] nam_len Length of the node name.
   * @param[out] parent_times Number of ".." in the path. Must be 0 on the first call,
   *                          must not be changed between consecutive calls.
   * @return Number of characters successfully parsed,
   *         positive on success, negative on failure.
   */
  static int
  parse_path_key_expr(const char *id, const char **prefix, int *pref_len, const char **name, int *nam_len,
                      int *parent_times)
  {
      int parsed = 0, ret, par_times = 0;
  
      assert(id);
      assert(parent_times);
      if (prefix) {
          *prefix = NULL;
      }
      if (pref_len) {
          *pref_len = 0;
      }
      if (name) {
          *name = NULL;
      }
      if (nam_len) {
          *nam_len = 0;
      }
  
      if (!*parent_times) {
          /* current-function-invocation *WSP "/" *WSP rel-path-keyexpr */
          if (strncmp(id, "current()", 9)) {
              return -parsed;
          }
  
          parsed += 9;
          id += 9;
  
          while (isspace(id[0])) {
              ++parsed;
              ++id;
          }
  
          if (id[0] != '/') {
              return -parsed;
          }
  
          ++parsed;
          ++id;
  
          while (isspace(id[0])) {
              ++parsed;
              ++id;
          }
  
          /* rel-path-keyexpr */
          if (strncmp(id, "..", 2)) {
              return -parsed;
          }
          ++par_times;
  
          parsed += 2;
          id += 2;
  
          while (isspace(id[0])) {
              ++parsed;
              ++id;
          }
      }
  
      /* 1*(".." *WSP "/" *WSP) *(node-identifier *WSP "/" *WSP) node-identifier
       *
       * first parent reference with whitespaces already parsed
       */
      if (id[0] != '/') {
          return -parsed;
      }
  
      ++parsed;
      ++id;
  
      while (isspace(id[0])) {
          ++parsed;
          ++id;
      }
  
      while (!strncmp(id, "..", 2) && !*parent_times) {
          ++par_times;
  
          parsed += 2;
          id += 2;
  
          while (isspace(id[0])) {
              ++parsed;
              ++id;
          }
  
          if (id[0] != '/') {
              return -parsed;
          }
  
          ++parsed;
          ++id;
  
          while (isspace(id[0])) {
              ++parsed;
              ++id;
          }
      }
  
      if (!*parent_times) {
          *parent_times = par_times;
      }
  
      /* all parent references must be parsed at this point */
      if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len, NULL, 0)) < 1) {
          return -parsed + ret;
      }
  
      parsed += ret;
      id += ret;
  
      return parsed;
  }
  
  /**
   * @brief Parse path-arg (leafref).
   *
   * path-arg            = absolute-path / relative-path
   * absolute-path       = 1*("/" (node-identifier *path-predicate))
   * relative-path       = 1*(".." "/") descendant-path
   *
   * @param[in] mod Module of the context node to get correct prefix in case it is not explicitly specified
   * @param[in] id Identifier to use.
   * @param[out] prefix Points to the prefix, NULL if there is not any.
   * @param[out] pref_len Length of the prefix, 0 if there is not any.
   * @param[out] name Points to the node name.
   * @param[out] nam_len Length of the node name.
   * @param[out] parent_times Number of ".." in the path. Must be 0 on the first call,
   *                          must not be changed between consecutive calls. -1 if the
   *                          path is relative.
   * @param[out] has_predicate Flag to mark whether there is a predicate specified.
   *
   * @return Number of characters successfully parsed,
   *         positive on success, negative on failure.
   */
  static int
  parse_path_arg(const struct lys_module *mod, const char *id, const char **prefix, int *pref_len,
                 const char **name, int *nam_len, int *parent_times, int *has_predicate)
  {
      int parsed = 0, ret, par_times = 0;
  
      assert(id);
      assert(parent_times);
      if (prefix) {
          *prefix = NULL;
      }
      if (pref_len) {
          *pref_len = 0;
      }
      if (name) {
          *name = NULL;
      }
      if (nam_len) {
          *nam_len = 0;
      }
      if (has_predicate) {
          *has_predicate = 0;
      }
  
      if (!*parent_times && !strncmp(id, "..", 2)) {
          ++par_times;
  
          parsed += 2;
          id += 2;
  
          while (!strncmp(id, "/..", 3)) {
              ++par_times;
  
              parsed += 3;
              id += 3;
          }
      }
  
      if (!*parent_times) {
          if (par_times) {
              *parent_times = par_times;
          } else {
              *parent_times = -1;
          }
      }
  
      if (id[0] != '/') {
          return -parsed;
      }
  
      /* skip '/' */
      ++parsed;
      ++id;
  
      /* node-identifier ([prefix:]identifier) */
      if ((ret = parse_node_identifier(id, prefix, pref_len, name, nam_len, NULL, 0)) < 1) {
          return -parsed + ret;
      }
      if (prefix && !(*prefix)) {
          /* actually we always need prefix even it is not specified */
          *prefix = lys_main_module(mod)->name;
          *pref_len = strlen(*prefix);
      }
  
      parsed += ret;
      id += ret;
  
      /* there is no predicate */
      if ((id[0] == '/') || !id[0]) {
          return parsed;
      } else if (id[0] != '[') {
          return -parsed;
      }
  
      if (has_predicate) {
          *has_predicate = 1;
      }
  
      return parsed;
  }
  
  /**
   * @brief Parse instance-identifier in JSON data format. That means that prefixes
   *        are actually model names.
   *
   * instance-identifier = 1*("/" (node-identifier *predicate))
   *
   * @param[in] id Identifier to use.
   * @param[out] model Points to the model name.
   * @param[out] mod_len Length of the model name.
   * @param[out] name Points to the node name.
   * @param[out] nam_len Length of the node name.
   * @param[out] has_predicate Flag to mark whether there is a predicate specified.
   *
   * @return Number of characters successfully parsed,
   *         positive on success, negative on failure.
   */
  static int
  parse_instance_identifier(const char *id, const char **model, int *mod_len, const char **name, int *nam_len,
                            int *has_predicate)
  {
      int parsed = 0, ret;
  
      assert(id && model && mod_len && name && nam_len);
  
      if (has_predicate) {
          *has_predicate = 0;
      }
  
      if (id[0] != '/') {
          return -parsed;
      }
  
      ++parsed;
      ++id;
  
      if ((ret = parse_identifier(id)) < 1) {
          return ret;
      }
  
      *name = id;
      *nam_len = ret;
  
      *model = NULL;
      *mod_len = 0;
  
      parsed += ret;
      id += ret;
  
      if (id[0] == ':') {
          /* we have prefix */
          *model = *name;
          *mod_len = *nam_len;
  
          ++parsed;
          ++id;
  
          if ((ret = parse_identifier(id)) < 1) {
              return ret;
          }
  
          *name = id;
          *nam_len = ret;
  
          parsed += ret;
          id += ret;
      }
  
      if (id[0] == '[' && has_predicate) {
          *has_predicate = 1;
      }
  
      return parsed;
  }
  
  /**
   * @brief Parse predicate (instance-identifier) in JSON data format. That means that prefixes
   *        (which are mandatory) are actually model names.
   *
   * predicate           = "[" *WSP (predicate-expr / pos) *WSP "]"
   * predicate-expr      = (node-identifier / ".") *WSP "=" *WSP
   *                       ((DQUOTE string DQUOTE) /
   *                        (SQUOTE string SQUOTE))
   * pos                 = non-negative-integer-value
   *
   * @param[in] id Identifier to use.
   * @param[out] model Points to the model name.
   * @param[out] mod_len Length of the model name.
   * @param[out] name Points to the node name. Can be identifier (from node-identifier), "." or pos.
   * @param[out] nam_len Length of the node name.
   * @param[out] value Value the node-identifier must have (string from the grammar),
   *                   NULL if there is not any.
   * @param[out] val_len Length of the value, 0 if there is not any.
   * @param[out] has_predicate Flag to mark whether there is a predicate specified.
   *
   * @return Number of characters successfully parsed,
   *         positive on success, negative on failure.
   */
  static int
  parse_predicate(const char *id, const char **model, int *mod_len, const char **name, int *nam_len,
                  const char **value, int *val_len, int *has_predicate)
  {
      const char *ptr;
      int parsed = 0, ret;
      char quote;
  
      assert(id);
      if (model) {
          assert(mod_len);
          *model = NULL;
          *mod_len = 0;
      }
      if (name) {
          assert(nam_len);
          *name = NULL;
          *nam_len = 0;
      }
      if (value) {
          assert(val_len);
          *value = NULL;
          *val_len = 0;
      }
      if (has_predicate) {
          *has_predicate = 0;
      }
  
      if (id[0] != '[') {
          return -parsed;
      }
  
      ++parsed;
      ++id;
  
      while (isspace(id[0])) {
          ++parsed;
          ++id;
      }
  
      /* pos */
      if (isdigit(id[0])) {
          if (name) {
              *name = id;
          }
  
          if (id[0] == '0') {
              return -parsed;
          }
  
          while (isdigit(id[0])) {
              ++parsed;
              ++id;
          }
  
          if (nam_len) {
              *nam_len = id-(*name);
          }
  
      /* "." or node-identifier */
      } else {
          if (id[0] == '.') {
              if (name) {
                  *name = id;
              }
              if (nam_len) {
                  *nam_len = 1;
              }
  
              ++parsed;
              ++id;
  
          } else {
              if ((ret = parse_node_identifier(id, model, mod_len, name, nam_len, NULL, 0)) < 1) {
                  return -parsed + ret;
              }
  
              parsed += ret;
              id += ret;
          }
  
          while (isspace(id[0])) {
              ++parsed;
              ++id;
          }
  
          if (id[0] != '=') {
              return -parsed;
          }
  
          ++parsed;
          ++id;
  
          while (isspace(id[0])) {
              ++parsed;
              ++id;
          }
  
          /* ((DQUOTE string DQUOTE) / (SQUOTE string SQUOTE)) */
          if ((id[0] == '\"') || (id[0] == '\'')) {
              quote = id[0];
  
              ++parsed;
              ++id;
  
              for (ptr = id; ptr && ptr[0] != quote; ++ptr) {
                  if (ptr[0] == '\\') {
                      ++ptr;
                  }
              }
              if (!ptr) {
                  return -parsed;
              }
              ret = ptr - id;
  
              if (value) {
                  *value = id;
              }
              if (val_len) {
                  *val_len = ret;
              }
  
              parsed += ret + 1;
              id += ret + 1;
          } else {
              return -parsed;
          }
      }
  
      while (isspace(id[0])) {
          ++parsed;
          ++id;
      }
  
      if (id[0] != ']') {
          return -parsed;
      }
  
      ++parsed;
      ++id;
  
      if ((id[0] == '[') && has_predicate) {
          *has_predicate = 1;
      }
  
      return parsed;
  }
  
  /**
   * @brief Parse schema-nodeid.
   *
   * schema-nodeid       = absolute-schema-nodeid /
   *                       descendant-schema-nodeid
   * absolute-schema-nodeid = 1*("/" node-identifier)
   * descendant-schema-nodeid = ["." "/"]
   *                       node-identifier
   *                       absolute-schema-nodeid
   *
   * @param[in] id Identifier to use.
   * @param[out] mod_name Points to the module name, NULL if there is not any.
   * @param[out] mod_name_len Length of the module name, 0 if there is not any.
   * @param[out] name Points to the node name.
   * @param[out] nam_len Length of the node name.
   * @param[out] is_relative Flag to mark whether the nodeid is absolute or descendant. Must be -1
   *                         on the first call, must not be changed between consecutive calls.
   * @param[out] has_predicate Flag to mark whether there is a predicate specified. It cannot be
   *                           based on the grammar, in those cases use NULL.
   * @param[in] extended Whether to accept an extended path (support for /[prefix:]*, //[prefix:]*, //[prefix:].).
   *
   * @return Number of characters successfully parsed,
   *         positive on success, negative on failure.
   */
  int
  parse_schema_nodeid(const char *id, const char **mod_name, int *mod_name_len, const char **name, int *nam_len,
                      int *is_relative, int *has_predicate, int *all_desc, int extended)
  {
      int parsed = 0, ret;
  
      assert(id);
      assert(is_relative);
  
      if (has_predicate) {
          *has_predicate = 0;
      }
  
      if (id[0] != '/') {
          if (*is_relative != -1) {
              return -parsed;
          } else {
              *is_relative = 1;
          }
          if (!strncmp(id, "./", 2)) {
              parsed += 2;
              id += 2;
          }
      } else {
          if (*is_relative == -1) {
              *is_relative = 0;
          }
          ++parsed;
          ++id;
      }
  
      if ((ret = parse_node_identifier(id, mod_name, mod_name_len, name, nam_len, all_desc, extended)) < 1) {
          return -parsed + ret;
      }
  
      parsed += ret;
      id += ret;
  
      if ((id[0] == '[') && has_predicate) {
          *has_predicate = 1;
      }
  
      return parsed;
  }
  
  /**
   * @brief Parse schema predicate (special format internally used).
   *
   * predicate           = "[" *WSP predicate-expr *WSP "]"
   * predicate-expr      = "." / [prefix:]identifier / positive-integer / key-with-value
   * key-with-value      = identifier *WSP "=" *WSP
   *                       ((DQUOTE string DQUOTE) /
   *                        (SQUOTE string SQUOTE))
   *
   * @param[in] id Identifier to use.
   * @param[out] mod_name Points to the list key module name.
   * @param[out] mod_name_len Length of \p mod_name.
   * @param[out] name Points to the list key name.
   * @param[out] nam_len Length of \p name.
   * @param[out] value Points to the key value. If specified, key-with-value is expected.
   * @param[out] val_len Length of \p value.
   * @param[out] has_predicate Flag to mark whether there is another predicate specified.
   */
  int
  parse_schema_json_predicate(const char *id, const char **mod_name, int *mod_name_len, const char **name, int *nam_len,
                              const char **value, int *val_len, int *has_predicate)
  {
      const char *ptr;
      int parsed = 0, ret;
      char quote;
  
      assert(id);
      if (mod_name) {
          *mod_name = NULL;
      }
      if (mod_name_len) {
          *mod_name_len = 0;
      }
      if (name) {
          *name = NULL;
      }
      if (nam_len) {
          *nam_len = 0;
      }
      if (value) {
          *value = NULL;
      }
      if (val_len) {
          *val_len = 0;
      }
      if (has_predicate) {
          *has_predicate = 0;
      }
  
      if (id[0] != '[') {
          return -parsed;
      }
  
      ++parsed;
      ++id;
  
      while (isspace(id[0])) {
          ++parsed;
          ++id;
      }
  
      /* identifier */
      if (id[0] == '.') {
          ret = 1;
  
          if (name) {
              *name = id;
          }
          if (nam_len) {
              *nam_len = ret;
          }
      } else if (isdigit(id[0])) {
          if (id[0] == '0') {
              return -parsed;
          }
          ret = 1;
          while (isdigit(id[ret])) {
              ++ret;
          }
  
          if (name) {
              *name = id;
          }
          if (nam_len) {
              *nam_len = ret;
          }
      } else if ((ret = parse_node_identifier(id, mod_name, mod_name_len, name, nam_len, NULL, 0)) < 1) {
          return -parsed + ret;
      }
  
      parsed += ret;
      id += ret;
  
      while (isspace(id[0])) {
          ++parsed;
          ++id;
      }
  
      /* there is value as well */
      if (id[0] == '=') {
          if (name && isdigit(**name)) {
              return -parsed;
          }
  
          ++parsed;
          ++id;
  
          while (isspace(id[0])) {
              ++parsed;
              ++id;
          }
  
          /* ((DQUOTE string DQUOTE) / (SQUOTE string SQUOTE)) */
          if ((id[0] == '\"') || (id[0] == '\'')) {
              quote = id[0];
  
              ++parsed;
              ++id;
  
              for (ptr = id; ptr && ptr[0] != quote; ++ptr) {
                  if (ptr[0] == '\\') {
                      ++ptr;
                  }
              }
              if (!ptr) {
                  return -parsed;
              }
              ret = ptr - id;
  
              if (value) {
                  *value = id;
              }
              if (val_len) {
                  *val_len = ret;
              }
  
              parsed += ret + 1;
              id += ret + 1;
          } else {
              return -parsed;
          }
  
          while (isspace(id[0])) {
              ++parsed;
              ++id;
          }
      }
  
      if (id[0] != ']') {
          return -parsed;
      }
  
      ++parsed;
      ++id;
  
      if ((id[0] == '[') && has_predicate) {
          *has_predicate = 1;
      }
  
      return parsed;
  }
  
  static struct lyd_node *
  resolve_json_data_node_hash(struct lyd_node *siblings, struct parsed_pred pp)
  {
      struct lyd_node *ret = NULL;
      char *key_or_value = NULL;
  
      if (pp.schema->nodetype == LYS_LEAFLIST) {
          assert((pp.len == 1) && (pp.pred[0].name[0] == '.') && (pp.pred[0].nam_len == 1));
  
          key_or_value = strndup(pp.pred[0].value, pp.pred[0].val_len);
      } else if (pp.schema->nodetype == LYS_LIST) {
          key_or_value = strndup(pp.pred_str, pp.pred_str_len);
      }
  
      /* try to find the node */
      lyd_find_sibling_val(siblings, pp.schema, key_or_value, &ret);
      free(key_or_value);
  
      return ret;
  }
  
  /**
   * @brief Resolve (find) a feature definition. Logs directly.
   *
   * @param[in] feat_name Feature name to resolve.
   * @param[in] len Length of \p feat_name.
   * @param[in] node Node with the if-feature expression.
   * @param[out] feature Pointer to be set to point to the feature definition, if feature not found
   * (return code 1), the pointer is untouched.
   *
   * @return 0 on success, 1 on forward reference, -1 on error.
   */
  static int
  resolve_feature(const char *feat_name, uint16_t len, const struct lys_node *node, struct lys_feature **feature)
  {
      char *str;
      const char *mod_name, *name;
      int mod_name_len, nam_len, i, j;
      const struct lys_module *module;
  
      assert(feature);
  
      /* check prefix */
      if ((i = parse_node_identifier(feat_name, &mod_name, &mod_name_len, &name, &nam_len, NULL, 0)) < 1) {
          LOGVAL(node->module->ctx, LYE_INCHAR, LY_VLOG_NONE, NULL, feat_name[-i], &feat_name[-i]);
          return -1;
      }
  
      module = lyp_get_module(lys_node_module(node), NULL, 0, mod_name, mod_name_len, 0);
      if (!module) {
          /* identity refers unknown data model */
          LOGVAL(node->module->ctx, LYE_INMOD_LEN, LY_VLOG_NONE, NULL, mod_name_len, mod_name);
          return -1;
      }
  
      if (module != node->module && module == lys_node_module(node)) {
          /* first, try to search directly in submodule where the feature was mentioned */
          for (j = 0; j < node->module->features_size; j++) {
              if (!strncmp(name, node->module->features[j].name, nam_len) && !node->module->features[j].name[nam_len]) {
                  /* check status */
                  if (lyp_check_status(node->flags, lys_node_module(node), node->name, node->module->features[j].flags,
                                       node->module->features[j].module, node->module->features[j].name, NULL)) {
                      return -1;
                  }
                  *feature = &node->module->features[j];
                  return 0;
              }
          }
      }
  
      /* search in the identified module ... */
      for (j = 0; j < module->features_size; j++) {
          if (!strncmp(name, module->features[j].name, nam_len) && !module->features[j].name[nam_len]) {
              /* check status */
              if (lyp_check_status(node->flags, lys_node_module(node), node->name, module->features[j].flags,
                                   module->features[j].module, module->features[j].name, NULL)) {
                  return -1;
              }
              *feature = &module->features[j];
              return 0;
          }
      }
      /* ... and all its submodules */
      for (i = 0; i < module->inc_size && module->inc[i].submodule; i++) {
          for (j = 0; j < module->inc[i].submodule->features_size; j++) {
              if (!strncmp(name, module->inc[i].submodule->features[j].name, nam_len)
                      && !module->inc[i].submodule->features[j].name[nam_len]) {
                  /* check status */
                  if (lyp_check_status(node->flags, lys_node_module(node), node->name,
                                       module->inc[i].submodule->features[j].flags,
                                       module->inc[i].submodule->features[j].module,
                                       module->inc[i].submodule->features[j].name, NULL)) {
                      return -1;
                  }
                  *feature = &module->inc[i].submodule->features[j];
                  return 0;
              }
          }
      }
  
      /* not found */
      str = strndup(feat_name, len);
      LOGVAL(node->module->ctx, LYE_INRESOLV, LY_VLOG_NONE, NULL, "feature", str);
      free(str);
      return 1;
  }
  
  /*
   * @return
   *  -  1 if enabled
   *  -  0 if disabled
   */
  static int
  resolve_feature_value(const struct lys_feature *feat)
  {
      int i;
  
      for (i = 0; i < feat->iffeature_size; i++) {
          if (!resolve_iffeature(&feat->iffeature[i])) {
              return 0;
          }
      }
  
      return feat->flags & LYS_FENABLED ? 1 : 0;
  }
  
  static int
  resolve_iffeature_recursive(struct lys_iffeature *expr, int *index_e, int *index_f)
  {
      uint8_t op;
      int a, b;
  
      op = iff_getop(expr->expr, *index_e);
      (*index_e)++;
  
      switch (op) {
      case LYS_IFF_F:
          /* resolve feature */
          return resolve_feature_value(expr->features[(*index_f)++]);
      case LYS_IFF_NOT:
          /* invert result */
          return resolve_iffeature_recursive(expr, index_e, index_f) ? 0 : 1;
      case LYS_IFF_AND:
      case LYS_IFF_OR:
          a = resolve_iffeature_recursive(expr, index_e, index_f);
          b = resolve_iffeature_recursive(expr, index_e, index_f);
          if (op == LYS_IFF_AND) {
              return a && b;
          } else { /* LYS_IFF_OR */
              return a || b;
          }
      }
  
      return 0;
  }
  
  int
  resolve_iffeature(struct lys_iffeature *expr)
  {
      int index_e = 0, index_f = 0;
  
      if (expr->expr && expr->features[0]) {
          return resolve_iffeature_recursive(expr, &index_e, &index_f);
      }
      return 0;
  }
  
  struct iff_stack {
      int size;
      int index;     /* first empty item */
      uint8_t *stack;
  };
  
  static int
  iff_stack_push(struct iff_stack *stack, uint8_t value)
  {
      if (stack->index == stack->size) {
          stack->size += 4;
          stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
          LY_CHECK_ERR_RETURN(!stack->stack, LOGMEM(NULL); stack->size = 0, EXIT_FAILURE);
      }
  
      stack->stack[stack->index++] = value;
      return EXIT_SUCCESS;
  }
  
  static uint8_t
  iff_stack_pop(struct iff_stack *stack)
  {
      stack->index--;
      return stack->stack[stack->index];
  }
  
  static void
  iff_stack_clean(struct iff_stack *stack)
  {
      stack->size = 0;
      free(stack->stack);
  }
  
  static void
  iff_setop(uint8_t *list, uint8_t op, int pos)
  {
      uint8_t *item;
      uint8_t mask = 3;
  
      assert(pos >= 0);
      assert(op <= 3); /* max 2 bits */
  
      item = &list[pos / 4];
      mask = mask << 2 * (pos % 4);
      *item = (*item) & ~mask;
      *item = (*item) | (op << 2 * (pos % 4));
  }
  
  uint8_t
  iff_getop(uint8_t *list, int pos)
  {
      uint8_t *item;
      uint8_t mask = 3, result;
  
      assert(pos >= 0);
  
      item = &list[pos / 4];
      result = (*item) & (mask << 2 * (pos % 4));
      return result >> 2 * (pos % 4);
  }
  
  #define LYS_IFF_LP 0x04 /* ( */
  #define LYS_IFF_RP 0x08 /* ) */
  
  /* internal structure for passing data for UNRES_IFFEAT */
  struct unres_iffeat_data {
      struct lys_node *node;
      const char *fname;
      int infeature;
  };
  
  void
  resolve_iffeature_getsizes(struct lys_iffeature *iffeat, unsigned int *expr_size, unsigned int *feat_size)
  {
      unsigned int e = 0, f = 0, r = 0;
      uint8_t op;
  
      assert(iffeat);
  
      if (!iffeat->expr) {
          goto result;
      }
  
      do {
          op = iff_getop(iffeat->expr, e++);
          switch (op) {
          case LYS_IFF_NOT:
              if (!r) {
                  r += 1;
              }
              break;
          case LYS_IFF_AND:
          case LYS_IFF_OR:
              if (!r) {
                  r += 2;
              } else {
                  r += 1;
              }
              break;
          case LYS_IFF_F:
              f++;
              if (r) {
                  r--;
              }
              break;
          }
      } while(r);
  
  result:
      if (expr_size) {
          *expr_size = e;
      }
      if (feat_size) {
          *feat_size = f;
      }
  }
  
  int
  resolve_iffeature_compile(struct lys_iffeature *iffeat_expr, const char *value, struct lys_node *node,
                            int infeature, struct unres_schema *unres)
  {
      const char *c = value;
      int r, rc = EXIT_FAILURE;
      int i, j, last_not, checkversion = 0;
      unsigned int f_size = 0, expr_size = 0, f_exp = 1;
      uint8_t op;
      struct iff_stack stack = {0, 0, NULL};
      struct unres_iffeat_data *iff_data;
      struct ly_ctx *ctx = node->module->ctx;
  
      assert(c);
  
      if (isspace(c[0])) {
          LOGVAL(ctx, LYE_INCHAR, LY_VLOG_NONE, NULL, c[0], c);
          return EXIT_FAILURE;
      }
  
      /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
      for (i = j = last_not = 0; c[i]; i++) {
          if (c[i] == '(') {
              checkversion = 1;
              j++;
              continue;
          } else if (c[i] == ')') {
              j--;
              continue;
          } else if (isspace(c[i])) {
              continue;
          }
  
          if (!strncmp(&c[i], "not", r = 3) || !strncmp(&c[i], "and", r = 3) || !strncmp(&c[i], "or", r = 2)) {
              if (c[i + r] == '\0') {
                  LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "if-feature");
                  return EXIT_FAILURE;
              } else if (!isspace(c[i + r])) {
                  /* feature name starting with the not/and/or */
                  last_not = 0;
                  f_size++;
              } else if (c[i] == 'n') { /* not operation */
                  if (last_not) {
                      /* double not */
                      expr_size = expr_size - 2;
                      last_not = 0;
                  } else {
                      last_not = 1;
                  }
              } else { /* and, or */
                  f_exp++;
                  /* not a not operation */
                  last_not = 0;
              }
              i += r;
          } else {
              f_size++;
              last_not = 0;
          }
          expr_size++;
  
          while (!isspace(c[i])) {
              if (c[i] == '(') {
                  LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "if-feature");
                  return EXIT_FAILURE;
              } else if (!c[i] || c[i] == ')') {
                  i--;
                  break;
              }
              i++;
          }
      }
      if (j || f_exp != f_size) {
          /* not matching count of ( and ) */
          LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "if-feature");
          return EXIT_FAILURE;
      }
  
      if (checkversion || expr_size > 1) {
          /* check that we have 1.1 module */
          if (node->module->version != LYS_VERSION_1_1) {
              LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "if-feature");
              LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "YANG 1.1 if-feature expression found in 1.0 module.");
              return EXIT_FAILURE;
          }
      }
  
      /* allocate the memory */
      iffeat_expr->expr = calloc((j = (expr_size / 4) + ((expr_size % 4) ? 1 : 0)), sizeof *iffeat_expr->expr);
      iffeat_expr->features = calloc(f_size, sizeof *iffeat_expr->features);
      stack.stack = malloc(expr_size * sizeof *stack.stack);
      LY_CHECK_ERR_GOTO(!stack.stack || !iffeat_expr->expr || !iffeat_expr->features, LOGMEM(ctx), error);
      stack.size = expr_size;
      f_size--; expr_size--; /* used as indexes from now */
  
      for (i--; i >= 0; i--) {
          if (c[i] == ')') {
              /* push it on stack */
              iff_stack_push(&stack, LYS_IFF_RP);
              continue;
          } else if (c[i] == '(') {
              /* pop from the stack into result all operators until ) */
              while((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
                  iff_setop(iffeat_expr->expr, op, expr_size--);
              }
              continue;
          } else if (isspace(c[i])) {
              continue;
          }
  
          /* end operator or operand -> find beginning and get what is it */
          j = i + 1;
          while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
              i--;
          }
          i++; /* get back by one step */
  
          if (!strncmp(&c[i], "not", 3) && isspace(c[i + 3])) {
              if (stack.index && stack.stack[stack.index - 1] == LYS_IFF_NOT) {
                  /* double not */
                  iff_stack_pop(&stack);
              } else {
                  /* not has the highest priority, so do not pop from the stack
                   * as in case of AND and OR */
                  iff_stack_push(&stack, LYS_IFF_NOT);
              }
          } else if (!strncmp(&c[i], "and", 3) && isspace(c[i + 3])) {
              /* as for OR - pop from the stack all operators with the same or higher
               * priority and store them to the result, then push the AND to the stack */
              while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
                  op = iff_stack_pop(&stack);
                  iff_setop(iffeat_expr->expr, op, expr_size--);
              }
              iff_stack_push(&stack, LYS_IFF_AND);
          } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
              while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
                  op = iff_stack_pop(&stack);
                  iff_setop(iffeat_expr->expr, op, expr_size--);
              }
              iff_stack_push(&stack, LYS_IFF_OR);
          } else {
              /* feature name, length is j - i */
  
              /* add it to the result */
              iff_setop(iffeat_expr->expr, LYS_IFF_F, expr_size--);
  
              /* now get the link to the feature definition. Since it can be
               * forward referenced, we have to keep the feature name in auxiliary
               * structure passed into unres */
              iff_data = malloc(sizeof *iff_data);
              LY_CHECK_ERR_GOTO(!iff_data, LOGMEM(ctx), error);
              iff_data->node = node;
              iff_data->fname = lydict_insert(node->module->ctx, &c[i], j - i);
              iff_data->infeature = infeature;
              r = unres_schema_add_node(node->module, unres, &iffeat_expr->features[f_size], UNRES_IFFEAT,
                                        (struct lys_node *)iff_data);
              f_size--;
  
              if (r == -1) {
                  lydict_remove(node->module->ctx, iff_data->fname);
                  free(iff_data);
                  goto error;
              }
          }
      }
      while (stack.index) {
          op = iff_stack_pop(&stack);
          iff_setop(iffeat_expr->expr, op, expr_size--);
      }
  
      if (++expr_size || ++f_size) {
          /* not all expected operators and operands found */
          LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, value, "if-feature");
          rc = EXIT_FAILURE;
      } else {
          rc = EXIT_SUCCESS;
      }
  
  error:
      /* cleanup */
      iff_stack_clean(&stack);
  
      return rc;
  }
  
  /**
   * @brief Resolve (find) a data node based on a schema-nodeid.
   *
   * Used for resolving unique statements - so id is expected to be relative and local (without reference to a different
   * module).
   *
   */
  struct lyd_node *
  resolve_data_descendant_schema_nodeid(const char *nodeid, struct lyd_node *start)
  {
      char *str, *token, *p;
      struct lyd_node *result = NULL, *iter;
      const struct lys_node *schema = NULL;
  
      assert(nodeid && start);
  
      if (nodeid[0] == '/') {
          return NULL;
      }
  
      str = p = strdup(nodeid);
      LY_CHECK_ERR_RETURN(!str, LOGMEM(start->schema->module->ctx), NULL);
  
      while (p) {
          token = p;
          p = strchr(p, '/');
          if (p) {
              *p = '\0';
              p++;
          }
  
          if (p) {
              /* inner node */
              if (resolve_descendant_schema_nodeid(token, schema ? schema->child : start->schema,
                                                   LYS_CONTAINER | LYS_CHOICE | LYS_CASE | LYS_LEAF, 0, &schema)
                      || !schema) {
                  result = NULL;
                  break;
              }
  
              if (schema->nodetype & (LYS_CHOICE | LYS_CASE)) {
                  continue;
              }
          } else {
              /* final node */
              if (resolve_descendant_schema_nodeid(token, schema ? schema->child : start->schema, LYS_LEAF, 0, &schema)
                      || !schema) {
                  result = NULL;
                  break;
              }
          }
          LY_TREE_FOR(result ? result->child : start, iter) {
              if (iter->schema == schema) {
                  /* move in data tree according to returned schema */
                  result = iter;
                  break;
              }
          }
          if (!iter) {
              /* instance not found */
              result = NULL;
              break;
          }
      }
      free(str);
  
      return result;
  }
  
  int
  schema_nodeid_siblingcheck(const struct lys_node *sibling, const struct lys_module *cur_module, const char *mod_name,
                             int mod_name_len, const char *name, int nam_len)
  {
      const struct lys_module *prefix_mod;
  
      /* handle special names */
      if (name[0] == '*') {
          return 2;
      } else if (name[0] == '.') {
          return 3;
      }
  
      /* name check */
      if (strncmp(name, sibling->name, nam_len) || sibling->name[nam_len]) {
          return 1;
      }
  
      /* module check */
      if (mod_name) {
          prefix_mod = lyp_get_module(cur_module, NULL, 0, mod_name, mod_name_len, 0);
          if (!prefix_mod) {
              return -1;
          }
      } else {
          prefix_mod = cur_module;
      }
      if (prefix_mod != lys_node_module(sibling)) {
          return 1;
      }
  
      /* match */
      return 0;
  }
  
  /* keys do not have to be ordered and do not have to be all of them */
  static int
  resolve_extended_schema_nodeid_predicate(const char *nodeid, const struct lys_node *node,
                                           const struct lys_module *cur_module, int *nodeid_end)
  {
      int mod_len, nam_len, has_predicate, r, i;
      const char *model, *name;
      struct lys_node_list *list;
  
      if (!(node->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
          return 1;
      }
  
      list = (struct lys_node_list *)node;
      do {
          r = parse_schema_json_predicate(nodeid, &model, &mod_len, &name, &nam_len, NULL, NULL, &has_predicate);
          if (r < 1) {
              LOGVAL(cur_module->ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, nodeid[r], &nodeid[r]);
              return -1;
          }
          nodeid += r;
  
          if (node->nodetype == LYS_LEAFLIST) {
              /* just check syntax */
              if (model || !name || (name[0] != '.') || has_predicate) {
                  return 1;
              }
              break;
          } else {
              /* check the key */
              for (i = 0; i < list->keys_size; ++i) {
                  if (strncmp(list->keys[i]->name, name, nam_len) || list->keys[i]->name[nam_len]) {
                      continue;
                  }
                  if (model) {
                      if (strncmp(lys_node_module((struct lys_node *)list->keys[i])->name, model, mod_len)
                              || lys_node_module((struct lys_node *)list->keys[i])->name[mod_len]) {
                          continue;
                      }
                  } else {
                      if (lys_node_module((struct lys_node *)list->keys[i]) != cur_module) {
                          continue;
                      }
                  }
  
                  /* match */
                  break;
              }
  
              if (i == list->keys_size) {
                  return 1;
              }
          }
      } while (has_predicate);
  
      if (!nodeid[0]) {
          *nodeid_end = 1;
      }
      return 0;
  }
  
  /* start_parent - relative, module - absolute, -1 error (logged), EXIT_SUCCESS ok
   */
  int
  resolve_schema_nodeid(const char *nodeid, const struct lys_node *start_parent, const struct lys_module *cur_module,
                        struct ly_set **ret, int extended, int no_node_error)
  {
      const char *name, *mod_name, *id, *backup_mod_name = NULL, *yang_data_name = NULL;
      const struct lys_node *sibling, *next, *elem;
      struct lys_node_augment *last_aug;
      int r, nam_len, mod_name_len = 0, is_relative = -1, all_desc, has_predicate, nodeid_end = 0;
      int yang_data_name_len, backup_mod_name_len = 0;
      /* resolved import module from the start module, it must match the next node-name-match sibling */
      const struct lys_module *start_mod, *aux_mod = NULL;
      char *str;
      struct ly_ctx *ctx;
  
      assert(nodeid && (start_parent || cur_module) && ret);
      *ret = NULL;
  
      if (!cur_module) {
          cur_module = lys_node_module(start_parent);
      }
      ctx = cur_module->ctx;
      id = nodeid;
  
      r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 1);
      if (r < 1) {
          LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
          return -1;
      }
  
      if (name[0] == '#') {
          if (is_relative) {
              LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, '#', name);
              return -1;
          }
          yang_data_name = name + 1;
          yang_data_name_len = nam_len - 1;
          backup_mod_name = mod_name;
          backup_mod_name_len = mod_name_len;
          id += r;
      } else {
          is_relative = -1;
      }
  
      r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate,
                              (extended ? &all_desc : NULL), extended);
      if (r < 1) {
          LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
          return -1;
      }
      id += r;
  
      if (backup_mod_name) {
          mod_name = backup_mod_name;
          mod_name_len = backup_mod_name_len;
      }
  
      if (is_relative && !start_parent) {
          LOGVAL(ctx, LYE_SPEC, LY_VLOG_STR, nodeid, "Starting node must be provided for relative paths.");
          return -1;
      }
  
      /* descendant-schema-nodeid */
      if (is_relative) {
          cur_module = start_mod = lys_node_module(start_parent);
  
      /* absolute-schema-nodeid */
      } else {
          start_mod = lyp_get_module(cur_module, NULL, 0, mod_name, mod_name_len, 0);
          if (!start_mod) {
              str = strndup(mod_name, mod_name_len);
              LOGVAL(ctx, LYE_PATH_INMOD, LY_VLOG_STR, str);
              free(str);
              return -1;
          }
          start_parent = NULL;
          if (yang_data_name) {
              start_parent = lyp_get_yang_data_template(start_mod, yang_data_name, yang_data_name_len);
              if (!start_parent) {
                  str = strndup(nodeid, (yang_data_name + yang_data_name_len) - nodeid);
                  LOGVAL(ctx, LYE_PATH_INNODE, LY_VLOG_STR, str);
                  free(str);
                  return -1;
              }
          }
      }
  
      while (1) {
          sibling = NULL;
          last_aug = NULL;
  
          if (start_parent) {
              if (mod_name && (strncmp(mod_name, cur_module->name, mod_name_len)
                      || (mod_name_len != (signed)strlen(cur_module->name)))) {
                  /* we are getting into another module (augment) */
                  aux_mod = lyp_get_module(cur_module, NULL, 0, mod_name, mod_name_len, 0);
                  if (!aux_mod) {
                      str = strndup(mod_name, mod_name_len);
                      LOGVAL(ctx, LYE_PATH_INMOD, LY_VLOG_STR, str);
                      free(str);
                      return -1;
                  }
              } else {
                  /* there is no mod_name, so why are we checking augments again?
                   * because this module may be not implemented and it augments something in another module and
                   * there is another augment augmenting that previous one */
                  aux_mod = cur_module;
              }
  
              /* look into augments */
              if (!extended) {
  get_next_augment:
                  last_aug = lys_getnext_target_aug(last_aug, aux_mod, start_parent);
              }
          }
  
          while ((sibling = lys_getnext(sibling, (last_aug ? (struct lys_node *)last_aug : start_parent), start_mod,
                  LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHINOUT | LYS_GETNEXT_PARENTUSES | LYS_GETNEXT_NOSTATECHECK))) {
              r = schema_nodeid_siblingcheck(sibling, cur_module, mod_name, mod_name_len, name, nam_len);
  
              /* resolve predicate */
              if (extended && ((r == 0) || (r == 2) || (r == 3)) && has_predicate) {
                  r = resolve_extended_schema_nodeid_predicate(id, sibling, cur_module, &nodeid_end);
                  if (r == 1) {
                      continue;
                  } else if (r == -1) {
                      return -1;
                  }
              } else if (!id[0]) {
                  nodeid_end = 1;
              }
  
              if (r == 0) {
                  /* one matching result */
                  if (nodeid_end) {
                      *ret = ly_set_new();
                      LY_CHECK_ERR_RETURN(!*ret, LOGMEM(ctx), -1);
                      ly_set_add(*ret, (void *)sibling, LY_SET_OPT_USEASLIST);
                  } else {
                      if (sibling->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
                          return -1;
                      }
                      start_parent = sibling;
                  }
                  break;
              } else if (r == 1) {
                  continue;
              } else if (r == 2) {
                  /* "*" */
                  if (!*ret) {
                      *ret = ly_set_new();
                      LY_CHECK_ERR_RETURN(!*ret, LOGMEM(ctx), -1);
                  }
                  ly_set_add(*ret, (void *)sibling, LY_SET_OPT_USEASLIST);
                  if (all_desc) {
                      LY_TREE_DFS_BEGIN(sibling, next, elem) {
                          if (elem != sibling) {
                              ly_set_add(*ret, (void *)elem, LY_SET_OPT_USEASLIST);
                          }
  
                          LY_TREE_DFS_END(sibling, next, elem);
                      }
                  }
              } else if (r == 3) {
                  /* "." */
                  if (!*ret) {
                      *ret = ly_set_new();
                      LY_CHECK_ERR_RETURN(!*ret, LOGMEM(ctx), -1);
                      if (start_parent) {
                          ly_set_add(*ret, (void *)start_parent, LY_SET_OPT_USEASLIST);
                      }
                  }
                  ly_set_add(*ret, (void *)sibling, LY_SET_OPT_USEASLIST);
                  if (all_desc) {
                      LY_TREE_DFS_BEGIN(sibling, next, elem) {
                          if (elem != sibling) {
                              ly_set_add(*ret, (void *)elem, LY_SET_OPT_USEASLIST);
                          }
  
                          LY_TREE_DFS_END(sibling, next, elem);
                      }
                  }
              } else {
                  LOGINT(ctx);
                  return -1;
              }
          }
  
          /* skip predicate */
          if (extended && has_predicate) {
              while (id[0] == '[') {
                  id = strchr(id, ']');
                  if (!id) {
                      LOGINT(ctx);
                      return -1;
                  }
                  ++id;
              }
          }
  
          if (nodeid_end && ((r == 0) || (r == 2) || (r == 3))) {
              return EXIT_SUCCESS;
          }
  
          /* no match */
          if (!sibling) {
              if (last_aug) {
                  /* it still could be in another augment */
                  goto get_next_augment;
              }
              if (no_node_error) {
                  str = strndup(nodeid, (name - nodeid) + nam_len);
                  LOGVAL(ctx, LYE_PATH_INNODE, LY_VLOG_STR, str);
                  free(str);
                  return -1;
              }
              *ret = NULL;
              return EXIT_SUCCESS;
          }
  
          r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate,
                                  (extended ? &all_desc : NULL), extended);
          if (r < 1) {
              LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
              return -1;
          }
          id += r;
      }
  
      /* cannot get here */
      LOGINT(ctx);
      return -1;
  }
  
  /* unique, refine,
   * >0  - unexpected char on position (ret - 1),
   *  0  - ok (but ret can still be NULL),
   * -1  - error,
   * -2  - violated no_innerlist  */
  int
  resolve_descendant_schema_nodeid(const char *nodeid, const struct lys_node *start, int ret_nodetype,
                                   int no_innerlist, const struct lys_node **ret)
  {
      const char *name, *mod_name, *id;
      const struct lys_node *sibling, *start_parent;
      int r, nam_len, mod_name_len, is_relative = -1;
      /* resolved import module from the start module, it must match the next node-name-match sibling */
      const struct lys_module *module;
  
      assert(nodeid && ret);
      assert(!(ret_nodetype & (LYS_USES | LYS_AUGMENT | LYS_GROUPING)));
  
      if (!start) {
          /* leaf not found */
          return 0;
      }
  
      id = nodeid;
      module = lys_node_module(start);
  
      if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 0)) < 1) {
          return ((id - nodeid) - r) + 1;
      }
      id += r;
  
      if (!is_relative) {
          return -1;
      }
  
      start_parent = lys_parent(start);
      while ((start_parent->nodetype == LYS_USES) && lys_parent(start_parent)) {
          start_parent = lys_parent(start_parent);
      }
  
      while (1) {
          sibling = NULL;
          while ((sibling = lys_getnext(sibling, start_parent, module,
                  LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHINOUT | LYS_GETNEXT_PARENTUSES
                  | LYS_GETNEXT_NOSTATECHECK))) {
              r = schema_nodeid_siblingcheck(sibling, module, mod_name, mod_name_len, name, nam_len);
              if (r == 0) {
                  if (!id[0]) {
                      if (!(sibling->nodetype & ret_nodetype)) {
                          /* wrong node type, too bad */
                          continue;
                      }
                      *ret = sibling;
                      return EXIT_SUCCESS;
                  }
                  start_parent = sibling;
                  break;
              } else if (r == 1) {
                  continue;
              } else {
                  return -1;
              }
          }
  
          /* no match */
          if (!sibling) {
              *ret = NULL;
              return EXIT_SUCCESS;
          } else if (no_innerlist && sibling->nodetype == LYS_LIST) {
              *ret = NULL;
              return -2;
          }
  
          if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 0)) < 1) {
              return ((id - nodeid) - r) + 1;
          }
          id += r;
      }
  
      /* cannot get here */
      LOGINT(module->ctx);
      return -1;
  }
  
  /* choice default */
  int
  resolve_choice_default_schema_nodeid(const char *nodeid, const struct lys_node *start, const struct lys_node **ret)
  {
      /* cannot actually be a path */
      if (strchr(nodeid, '/')) {
          return -1;
      }
  
      return resolve_descendant_schema_nodeid(nodeid, start, LYS_NO_RPC_NOTIF_NODE, 0, ret);
  }
  
  /* uses, -1 error, EXIT_SUCCESS ok (but ret can still be NULL), >0 unexpected char on ret - 1 */
  static int
  resolve_uses_schema_nodeid(const char *nodeid, const struct lys_node *start, const struct lys_node_grp **ret)
  {
      const struct lys_module *module;
      const char *mod_prefix, *name;
      int i, mod_prefix_len, nam_len;
  
      /* parse the identifier, it must be parsed on one call */
      if (((i = parse_node_identifier(nodeid, &mod_prefix, &mod_prefix_len, &name, &nam_len, NULL, 0)) < 1) || nodeid[i]) {
          return -i + 1;
      }
  
      module = lyp_get_module(start->module, mod_prefix, mod_prefix_len, NULL, 0, 0);
      if (!module) {
          return -1;
      }
      if (module != lys_main_module(start->module)) {
          start = module->data;
      }
  
      *ret = lys_find_grouping_up(name, (struct lys_node *)start);
  
      return EXIT_SUCCESS;
  }
  
  int
  resolve_absolute_schema_nodeid(const char *nodeid, const struct lys_module *module, int ret_nodetype,
                                 const struct lys_node **ret)
  {
      const char *name, *mod_name, *id;
      const struct lys_node *sibling, *start_parent;
      int r, nam_len, mod_name_len, is_relative = -1;
      const struct lys_module *abs_start_mod;
  
      assert(nodeid && module && ret);
      assert(!(ret_nodetype & (LYS_USES | LYS_AUGMENT)) && ((ret_nodetype == LYS_GROUPING) || !(ret_nodetype & LYS_GROUPING)));
  
      id = nodeid;
      start_parent = NULL;
  
      if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 0)) < 1) {
          return ((id - nodeid) - r) + 1;
      }
      id += r;
  
      if (is_relative) {
          return -1;
      }
  
      abs_start_mod = lyp_get_module(module, NULL, 0, mod_name, mod_name_len, 0);
      if (!abs_start_mod) {
          return -1;
      }
  
      while (1) {
          sibling = NULL;
          while ((sibling = lys_getnext(sibling, start_parent, abs_start_mod, LYS_GETNEXT_WITHCHOICE
                  | LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHINOUT | LYS_GETNEXT_WITHGROUPING | LYS_GETNEXT_NOSTATECHECK))) {
              r = schema_nodeid_siblingcheck(sibling, module, mod_name, mod_name_len, name, nam_len);
              if (r == 0) {
                  if (!id[0]) {
                      if (!(sibling->nodetype & ret_nodetype)) {
                          /* wrong node type, too bad */
                          continue;
                      }
                      *ret = sibling;
                      return EXIT_SUCCESS;
                  }
                  start_parent = sibling;
                  break;
              } else if (r == 1) {
                  continue;
              } else {
                  return -1;
              }
          }
  
          /* no match */
          if (!sibling) {
              *ret = NULL;
              return EXIT_SUCCESS;
          }
  
          if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 0)) < 1) {
              return ((id - nodeid) - r) + 1;
          }
          id += r;
      }
  
      /* cannot get here */
      LOGINT(module->ctx);
      return -1;
  }
  
  static int
  resolve_json_schema_list_predicate(const char *predicate, const struct lys_node_list *list, int *parsed)
  {
      const char *mod_name, *name;
      int mod_name_len, nam_len, has_predicate, i;
      struct lys_node *key;
  
      if (((i = parse_schema_json_predicate(predicate, &mod_name, &mod_name_len, &name, &nam_len, NULL, NULL, &has_predicate)) < 1)
              || !strncmp(name, ".", nam_len)) {
          LOGVAL(list->module->ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, predicate[-i], &predicate[-i]);
          return -1;
      }
  
      predicate += i;
      *parsed += i;
  
      if (!isdigit(name[0])) {
          for (i = 0; i < list->keys_size; ++i) {
              key = (struct lys_node *)list->keys[i];
              if (!strncmp(key->name, name, nam_len) && !key->name[nam_len]) {
                  break;
              }
          }
  
          if (i == list->keys_size) {
              LOGVAL(list->module->ctx, LYE_PATH_INKEY, LY_VLOG_NONE, NULL, name);
              return -1;
          }
      }
  
      /* more predicates? */
      if (has_predicate) {
          return resolve_json_schema_list_predicate(predicate, list, parsed);
      }
  
      return 0;
  }
  
  /* cannot return LYS_GROUPING, LYS_AUGMENT, LYS_USES, logs directly */
  const struct lys_node *
  resolve_json_nodeid(const char *nodeid, const struct ly_ctx *ctx, const struct lys_node *start, int output)
  {
      char *str;
      const char *name, *mod_name, *id, *backup_mod_name = NULL, *yang_data_name = NULL;
      const struct lys_node *sibling, *start_parent, *parent;
      int r, nam_len, mod_name_len, is_relative = -1, has_predicate;
      int yang_data_name_len, backup_mod_name_len;
      /* resolved import module from the start module, it must match the next node-name-match sibling */
      const struct lys_module *prefix_mod, *module, *prev_mod;
  
      assert(nodeid && (ctx || start));
      if (!ctx) {
          ctx = start->module->ctx;
      }
  
      id = nodeid;
  
      if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 1)) < 1) {
          LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
          return NULL;
      }
  
      if (name[0] == '#') {
          if (is_relative) {
              LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, '#', name);
              return NULL;
          }
          yang_data_name = name + 1;
          yang_data_name_len = nam_len - 1;
          backup_mod_name = mod_name;
          backup_mod_name_len = mod_name_len;
          id += r;
      } else {
          is_relative = -1;
      }
  
      if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
          LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
          return NULL;
      }
      id += r;
  
      if (backup_mod_name) {
          mod_name = backup_mod_name;
          mod_name_len = backup_mod_name_len;
      }
  
      if (is_relative) {
          assert(start);
          start_parent = start;
          while (start_parent && (start_parent->nodetype == LYS_USES)) {
              start_parent = lys_parent(start_parent);
          }
          module = start->module;
      } else {
          if (!mod_name) {
              str = strndup(nodeid, (name + nam_len) - nodeid);
              LOGVAL(ctx, LYE_PATH_MISSMOD, LY_VLOG_STR, nodeid);
              free(str);
              return NULL;
          }
  
          str = strndup(mod_name, mod_name_len);
          module = ly_ctx_get_module(ctx, str, NULL, 1);
          free(str);
  
          if (!module) {
              str = strndup(nodeid, (mod_name + mod_name_len) - nodeid);
              LOGVAL(ctx, LYE_PATH_INMOD, LY_VLOG_STR, str);
              free(str);
              return NULL;
          }
          start_parent = NULL;
          if (yang_data_name) {
              start_parent = lyp_get_yang_data_template(module, yang_data_name, yang_data_name_len);
              if (!start_parent) {
                  str = strndup(nodeid, (yang_data_name + yang_data_name_len) - nodeid);
                  LOGVAL(ctx, LYE_PATH_INNODE, LY_VLOG_STR, str);
                  free(str);
                  return NULL;
              }
          }
  
          /* now it's as if there was no module name */
          mod_name = NULL;
          mod_name_len = 0;
      }
  
      prev_mod = module;
  
      while (1) {
          sibling = NULL;
          while ((sibling = lys_getnext(sibling, start_parent, module, 0))) {
              /* name match */
              if (sibling->name && !strncmp(name, sibling->name, nam_len) && !sibling->name[nam_len]) {
                  /* output check */
                  for (parent = lys_parent(sibling); parent && !(parent->nodetype & (LYS_INPUT | LYS_OUTPUT)); parent = lys_parent(parent));
                  if (parent) {
                      if (output && (parent->nodetype == LYS_INPUT)) {
                          continue;
                      } else if (!output && (parent->nodetype == LYS_OUTPUT)) {
                          continue;
                      }
                  }
  
                  /* module check */
                  if (mod_name) {
                      /* will also find an augment module */
                      prefix_mod = ly_ctx_nget_module(ctx, mod_name, mod_name_len, NULL, 1);
  
                      if (!prefix_mod) {
                          str = strndup(nodeid, (mod_name + mod_name_len) - nodeid);
                          LOGVAL(ctx, LYE_PATH_INMOD, LY_VLOG_STR, str);
                          free(str);
                          return NULL;
                      }
                  } else {
                      prefix_mod = prev_mod;
                  }
                  if (prefix_mod != lys_node_module(sibling)) {
                      continue;
                  }
  
                  /* do we have some predicates on it? */
                  if (has_predicate) {
                      r = 0;
                      if (sibling->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
                          if ((r = parse_schema_json_predicate(id, NULL, NULL, NULL, NULL, NULL, NULL, &has_predicate)) < 1) {
                              LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
                              return NULL;
                          }
                      } else if (sibling->nodetype == LYS_LIST) {
                          if (resolve_json_schema_list_predicate(id, (const struct lys_node_list *)sibling, &r)) {
                              return NULL;
                          }
                      } else {
                          LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[0], id);
                          return NULL;
                      }
                      id += r;
                  }
  
                  /* the result node? */
                  if (!id[0]) {
                      return sibling;
                  }
  
                  /* move down the tree, if possible */
                  if (sibling->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
                      LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[0], id);
                      return NULL;
                  }
                  start_parent = sibling;
  
                  /* update prev mod */
                  prev_mod = (start_parent->child ? lys_node_module(start_parent->child) : module);
                  break;
              }
          }
  
          /* no match */
          if (!sibling) {
              str = strndup(nodeid, (name + nam_len) - nodeid);
              LOGVAL(ctx, LYE_PATH_INNODE, LY_VLOG_STR, str);
              free(str);
              return NULL;
          }
  
          if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
              LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
              return NULL;
          }
          id += r;
      }
  
      /* cannot get here */
      LOGINT(ctx);
      return NULL;
  }
  
  static int
  resolve_partial_json_data_list_predicate(struct parsed_pred pp, struct lyd_node *node, int position)
  {
      uint16_t i;
      char *val_str;
      struct lyd_node_leaf_list *key;
      struct lys_node_list *slist;
      struct ly_ctx *ctx;
  
      assert(node);
      assert(node->schema->nodetype == LYS_LIST);
      assert(pp.len);
  
      ctx = node->schema->module->ctx;
      slist = (struct lys_node_list *)node->schema;
  
      /* is the predicate a number? */
      if (isdigit(pp.pred[0].name[0])) {
          if (position == atoi(pp.pred[0].name)) {
              /* match */
              return 0;
          } else {
              /* not a match */
              return 1;
          }
      }
  
      key = (struct lyd_node_leaf_list *)node->child;
      if (!key) {
          /* it is not a position, so we need a key for it to be a match */
          return 1;
      }
  
      /* go through all the keys */
      for (i = 0; i < slist->keys_size; ++i) {
          if (strncmp(key->schema->name, pp.pred[i].name, pp.pred[i].nam_len) || key->schema->name[pp.pred[i].nam_len]) {
              LOGVAL(ctx, LYE_PATH_INKEY, LY_VLOG_NONE, NULL, pp.pred[i].name);
              return -1;
          }
  
          if (pp.pred[i].mod_name) {
              /* specific module, check that the found key is from that module */
              if (strncmp(lyd_node_module((struct lyd_node *)key)->name, pp.pred[i].mod_name, pp.pred[i].mod_name_len)
                      || lyd_node_module((struct lyd_node *)key)->name[pp.pred[i].mod_name_len]) {
                  LOGVAL(ctx, LYE_PATH_INKEY, LY_VLOG_NONE, NULL, pp.pred[i].name);
                  return -1;
              }
  
              /* but if the module is the same as the parent, it should have been omitted */
              if (lyd_node_module((struct lyd_node *)key) == lyd_node_module(node)) {
                  LOGVAL(ctx, LYE_PATH_INKEY, LY_VLOG_NONE, NULL, pp.pred[i].name);
                  return -1;
              }
          } else {
              /* no module, so it must be the same as the list (parent) */
              if (lyd_node_module((struct lyd_node *)key) != lyd_node_module(node)) {
                  LOGVAL(ctx, LYE_PATH_INKEY, LY_VLOG_NONE, NULL, pp.pred[i].name);
                  return -1;
              }
          }
  
          /* get canonical value */
          val_str = lyd_make_canonical(key->schema, pp.pred[i].value, pp.pred[i].val_len);
          if (!val_str) {
              return -1;
          }
  
          /* value does not match */
          if (strcmp(key->value_str, val_str)) {
              free(val_str);
              return 1;
          }
          free(val_str);
  
          key = (struct lyd_node_leaf_list *)key->next;
      }
  
      return 0;
  }
  
  /**
   * @brief get the closest parent of the node (or the node itself) identified by the nodeid (path)
   *
   * @param[in] nodeid Node data path to find
   * @param[in] llist_value If the \p nodeid identifies leaf-list, this is expected value of the leaf-list instance.
   * @param[in] options Bitmask of options flags, see @ref pathoptions.
   * @param[out] parsed Number of characters processed in \p id
   * @return The closes parent (or the node itself) from the path
   */
  struct lyd_node *
  resolve_partial_json_data_nodeid(const char *nodeid, const char *llist_value, struct lyd_node *start, int options,
                                   int *parsed)
  {
      const char *id, *mod_name, *name, *data_val, *llval;
      int r, ret, mod_name_len, nam_len, is_relative = -1, list_instance_position;
      int has_predicate, last_parsed = 0, llval_len;
      struct lyd_node *sibling, *last_match = NULL;
      struct lyd_node_leaf_list *llist;
      const struct lys_module *prev_mod;
      struct ly_ctx *ctx;
      const struct lys_node *ssibling, *sparent;
      struct lys_node_list *slist;
      struct parsed_pred pp;
  
      assert(nodeid && start && parsed);
  
      memset(&pp, 0, sizeof pp);
      ctx = start->schema->module->ctx;
      id = nodeid;
  
      /* parse first nodeid in case it is yang-data extension */
      if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, NULL, NULL, 1)) < 1) {
          LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
          goto error;
      }
  
      if (name[0] == '#') {
          if (is_relative) {
              LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, '#', name);
              goto error;
          }
          id += r;
          last_parsed = r;
      } else {
          is_relative = -1;
      }
  
      /* parse first nodeid */
      if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
          LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
          goto error;
      }
      id += r;
      /* add it to parsed only after the data node was actually found */
      last_parsed += r;
  
      if (is_relative) {
          prev_mod = lyd_node_module(start);
          start = (start->schema->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_RPC | LYS_ACTION | LYS_NOTIF)) ? start->child : NULL;
      } else {
          for (; start->parent; start = start->parent);
          prev_mod = lyd_node_module(start);
      }
      if (!start) {
          /* there are no siblings to search */
          return NULL;
      }
  
      /* do not duplicate code, use predicate parsing from the loop */
      goto parse_predicates;
  
      while (1) {
          /* find the correct schema node first */
          ssibling = NULL;
          sparent = (start && start->parent) ? start->parent->schema : NULL;
          while ((ssibling = lys_getnext(ssibling, sparent, prev_mod, 0))) {
              /* skip invalid input/output nodes */
              if (sparent && (sparent->nodetype & (LYS_RPC | LYS_ACTION))) {
                  if (options & LYD_PATH_OPT_OUTPUT) {
                      if (lys_parent(ssibling)->nodetype == LYS_INPUT) {
                          continue;
                      }
                  } else {
                      if (lys_parent(ssibling)->nodetype == LYS_OUTPUT) {
                          continue;
                      }
                  }
              }
  
              if (!schema_nodeid_siblingcheck(ssibling, prev_mod, mod_name, mod_name_len, name, nam_len)) {
                  break;
              }
          }
          if (!ssibling) {
              /* there is not even such a schema node */
              free(pp.pred);
              return last_match;
          }
          pp.schema = ssibling;
  
          /* unify leaf-list value - it is possible to specify last-node value as both a predicate or parameter if
           * is a leaf-list, unify both cases and the value will in both cases be in the predicate structure */
          if (!id[0] && !pp.len && (ssibling->nodetype == LYS_LEAFLIST)) {
              pp.len = 1;
              pp.pred = calloc(1, sizeof *pp.pred);
              LY_CHECK_ERR_GOTO(!pp.pred, LOGMEM(ctx), error);
  
              pp.pred[0].name = ".";
              pp.pred[0].nam_len = 1;
              pp.pred[0].value = (llist_value ? llist_value : "");
              pp.pred[0].val_len = strlen(pp.pred[0].value);
          }
  
          if (ssibling->nodetype & (LYS_LEAFLIST | LYS_LEAF)) {
              /* check leaf/leaf-list predicate */
              if (pp.len > 1) {
                  LOGVAL(ctx, LYE_PATH_PREDTOOMANY, LY_VLOG_NONE, NULL);
                  goto error;
              } else if (pp.len) {
                  if ((pp.pred[0].name[0] != '.') || (pp.pred[0].nam_len != 1)) {
                      LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, pp.pred[0].name[0], pp.pred[0].name);
                      goto error;
                  }
                  if ((((struct lys_node_leaf *)ssibling)->type.base == LY_TYPE_IDENT) && !strnchr(pp.pred[0].value, ':', pp.pred[0].val_len)) {
                      LOGVAL(ctx, LYE_PATH_INIDENTREF, LY_VLOG_LYS, ssibling, pp.pred[0].val_len, pp.pred[0].value);
                      goto error;
                  }
              }
          } else if (ssibling->nodetype == LYS_LIST) {
              /* list should have predicates for all the keys or position */
              slist = (struct lys_node_list *)ssibling;
              if (!pp.len) {
                  /* none match */
                  return last_match;
              } else if (!isdigit(pp.pred[0].name[0])) {
                  /* list predicate is not a position, so there must be all the keys */
                  if (pp.len > slist->keys_size) {
                      LOGVAL(ctx, LYE_PATH_PREDTOOMANY, LY_VLOG_NONE, NULL);
                      goto error;
                  } else if (pp.len < slist->keys_size) {
                      LOGVAL(ctx, LYE_PATH_MISSKEY, LY_VLOG_NONE, NULL, slist->keys[pp.len]->name);
                      goto error;
                  }
                  /* check that all identityrefs have module name, otherwise the hash of the list instance will never match!! */
                  for (r = 0; r < pp.len; ++r) {
                      if ((slist->keys[r]->type.base == LY_TYPE_IDENT) && !strnchr(pp.pred[r].value, ':', pp.pred[r].val_len)) {
                          LOGVAL(ctx, LYE_PATH_INIDENTREF, LY_VLOG_LYS, slist->keys[r], pp.pred[r].val_len, pp.pred[r].value);
                          goto error;
                      }
                  }
              }
          } else if (pp.pred) {
              /* no other nodes allow predicates */
              LOGVAL(ctx, LYE_PATH_PREDTOOMANY, LY_VLOG_NONE, NULL);
              goto error;
          }
  
          /* we will not be matching list position, keyless lists, or state leaf-lists this way */
          if (((pp.schema->nodetype != LYS_LIST) || (((struct lys_node_list *)pp.schema)->keys_size && !isdigit(pp.pred[0].name[0])))
                  && ((pp.schema->nodetype != LYS_LEAFLIST) || (pp.schema->flags & LYS_CONFIG_W))) {
              sibling = resolve_json_data_node_hash(start, pp);
          } else {
              list_instance_position = 0;
              LY_TREE_FOR(start, sibling) {
                  /* RPC/action data check, return simply invalid argument, because the data tree is invalid */
                  if (lys_parent(sibling->schema)) {
                      if (options & LYD_PATH_OPT_OUTPUT) {
                          if (lys_parent(sibling->schema)->nodetype == LYS_INPUT) {
                              LOGERR(ctx, LY_EINVAL, "Provided data tree includes some RPC input nodes (%s).", sibling->schema->name);
                              goto error;
                          }
                      } else {
                          if (lys_parent(sibling->schema)->nodetype == LYS_OUTPUT) {
                              LOGERR(ctx, LY_EINVAL, "Provided data tree includes some RPC output nodes (%s).", sibling->schema->name);
                              goto error;
                          }
                      }
                  }
  
                  if (sibling->schema != ssibling) {
                      /* wrong schema node */
                      continue;
                  }
  
                  /* leaf-list, did we find it with the correct value or not? */
                  if (ssibling->nodetype == LYS_LEAFLIST) {
                      if (ssibling->flags & LYS_CONFIG_R) {
                          /* state leaf-lists will never match */
                          continue;
                      }
  
                      llist = (struct lyd_node_leaf_list *)sibling;
  
                      /* get the expected leaf-list value */
                      llval = NULL;
                      llval_len = 0;
                      if (pp.pred) {
                          /* it was already checked that it is correct */
                          llval = pp.pred[0].value;
                          llval_len = pp.pred[0].val_len;
  
                      }
  
                      /* make value canonical (remove module name prefix) unless it was specified with it */
                      if (llval && !strchr(llval, ':') && (llist->value_type & LY_TYPE_IDENT)
                              && !strncmp(llist->value_str, lyd_node_module(sibling)->name, strlen(lyd_node_module(sibling)->name))
                              && (llist->value_str[strlen(lyd_node_module(sibling)->name)] == ':')) {
                          data_val = llist->value_str + strlen(lyd_node_module(sibling)->name) + 1;
                      } else {
                          data_val = llist->value_str;
                      }
  
                      if ((!llval && data_val && data_val[0]) || (llval && (strncmp(llval, data_val, llval_len)
                              || data_val[llval_len]))) {
                          continue;
                      }
  
                  } else if (ssibling->nodetype == LYS_LIST) {
                      /* list, we likely need predicates'n'stuff then, but if without a predicate, we are always creating it */
                      ++list_instance_position;
                      ret = resolve_partial_json_data_list_predicate(pp, sibling, list_instance_position);
                      if (ret == -1) {
                          goto error;
                      } else if (ret == 1) {
                          /* this list instance does not match */
                          continue;
                      }
                  }
  
                  break;
              }
          }
  
          /* no match, return last match */
          if (!sibling) {
              free(pp.pred);
              return last_match;
          }
  
          /* we found a next matching node */
          *parsed += last_parsed;
          last_match = sibling;
          prev_mod = lyd_node_module(sibling);
  
          /* the result node? */
          if (!id[0]) {
              free(pp.pred);
              return last_match;
          }
  
          /* move down the tree, if possible, and continue */
          if (ssibling->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
              /* there can be no children even through expected, error */
              LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[0], id);
              goto error;
          } else if (!sibling->child) {
              /* there could be some children, but are not, return what we found so far */
              free(pp.pred);
              return last_match;
          }
          start = sibling->child;
  
          /* parse nodeid */
          if ((r = parse_schema_nodeid(id, &mod_name, &mod_name_len, &name, &nam_len, &is_relative, &has_predicate, NULL, 0)) < 1) {
              LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[-r], &id[-r]);
              goto error;
          }
          id += r;
          last_parsed = r;
  
  parse_predicates:
          /* parse all the predicates */
          free(pp.pred);
          pp.schema = NULL;
          pp.pred_str = NULL;
          pp.pred_str_len = 0;
          pp.len = 0;
          pp.pred = NULL;
          while (has_predicate) {
              if (!pp.pred_str) {
                  pp.pred_str = id;
              }
  
              ++pp.len;
              pp.pred = ly_realloc(pp.pred, pp.len * sizeof *pp.pred);
              LY_CHECK_ERR_GOTO(!pp.pred, LOGMEM(ctx), error);
              if ((r = parse_schema_json_predicate(id, &pp.pred[pp.len - 1].mod_name, &pp.pred[pp.len - 1].mod_name_len,
                                                   &pp.pred[pp.len - 1].name, &pp.pred[pp.len - 1].nam_len, &pp.pred[pp.len - 1].value,
                                                   &pp.pred[pp.len - 1].val_len, &has_predicate)) < 1) {
                  LOGVAL(ctx, LYE_PATH_INCHAR, LY_VLOG_NONE, NULL, id[0], id);
                  goto error;
              }
  
              id += r;
              last_parsed += r;
              pp.pred_str_len += r;
          }
      }
  
  error:
      *parsed = -1;
      free(pp.pred);
      return NULL;
  }
  
  /**
   * @brief Resolves length or range intervals. Does not log.
   * Syntax is assumed to be correct, *ret MUST be NULL.
   *
   * @param[in] ctx Context for errors.
   * @param[in] str_restr Restriction as a string.
   * @param[in] type Type of the restriction.
   * @param[out] ret Final interval structure that starts with
   * the interval of the initial type, continues with intervals
   * of any superior types derived from the initial one, and
   * finishes with intervals from our \p type.
   *
   * @return EXIT_SUCCESS on succes, -1 on error.
   */
  int
  resolve_len_ran_interval(struct ly_ctx *ctx, const char *str_restr, struct lys_type *type, struct len_ran_intv **ret)
  {
      /* 0 - unsigned, 1 - signed, 2 - floating point */
      int kind;
      int64_t local_smin = 0, local_smax = 0, local_fmin, local_fmax;
      uint64_t local_umin, local_umax = 0;
      uint8_t local_fdig = 0;
      const char *seg_ptr, *ptr;
      struct len_ran_intv *local_intv = NULL, *tmp_local_intv = NULL, *tmp_intv, *intv = NULL;
  
      switch (type->base) {
      case LY_TYPE_BINARY:
          kind = 0;
          local_umin = 0;
          local_umax = 18446744073709551615UL;
  
          if (!str_restr && type->info.binary.length) {
              str_restr = type->info.binary.length->expr;
          }
          break;
      case LY_TYPE_DEC64:
          kind = 2;
          local_fmin = __INT64_C(-9223372036854775807) - __INT64_C(1);
          local_fmax = __INT64_C(9223372036854775807);
          local_fdig = type->info.dec64.dig;
  
          if (!str_restr && type->info.dec64.range) {
              str_restr = type->info.dec64.range->expr;
          }
          break;
      case LY_TYPE_INT8:
          kind = 1;
          local_smin = __INT64_C(-128);
          local_smax = __INT64_C(127);
  
          if (!str_restr && type->info.num.range) {
              str_restr = type->info.num.range->expr;
          }
          break;
      case LY_TYPE_INT16:
          kind = 1;
          local_smin = __INT64_C(-32768);
          local_smax = __INT64_C(32767);
  
          if (!str_restr && type->info.num.range) {
              str_restr = type->info.num.range->expr;
          }
          break;
      case LY_TYPE_INT32:
          kind = 1;
          local_smin = __INT64_C(-2147483648);
          local_smax = __INT64_C(2147483647);
  
          if (!str_restr && type->info.num.range) {
              str_restr = type->info.num.range->expr;
          }
          break;
      case LY_TYPE_INT64:
          kind = 1;
          local_smin = __INT64_C(-9223372036854775807) - __INT64_C(1);
          local_smax = __INT64_C(9223372036854775807);
  
          if (!str_restr && type->info.num.range) {
              str_restr = type->info.num.range->expr;
          }
          break;
      case LY_TYPE_UINT8:
          kind = 0;
          local_umin = __UINT64_C(0);
          local_umax = __UINT64_C(255);
  
          if (!str_restr && type->info.num.range) {
              str_restr = type->info.num.range->expr;
          }
          break;
      case LY_TYPE_UINT16:
          kind = 0;
          local_umin = __UINT64_C(0);
          local_umax = __UINT64_C(65535);
  
          if (!str_restr && type->info.num.range) {
              str_restr = type->info.num.range->expr;
          }
          break;
      case LY_TYPE_UINT32:
          kind = 0;
          local_umin = __UINT64_C(0);
          local_umax = __UINT64_C(4294967295);
  
          if (!str_restr && type->info.num.range) {
              str_restr = type->info.num.range->expr;
          }
          break;
      case LY_TYPE_UINT64:
          kind = 0;
          local_umin = __UINT64_C(0);
          local_umax = __UINT64_C(18446744073709551615);
  
          if (!str_restr && type->info.num.range) {
              str_restr = type->info.num.range->expr;
          }
          break;
      case LY_TYPE_STRING:
          kind = 0;
          local_umin = __UINT64_C(0);
          local_umax = __UINT64_C(18446744073709551615);
  
          if (!str_restr && type->info.str.length) {
              str_restr = type->info.str.length->expr;
          }
          break;
      default:
          return -1;
      }
  
      /* process superior types */
      if (type->der) {
          if (resolve_len_ran_interval(ctx, NULL, &type->der->type, &intv)) {
              return -1;
          }
          assert(!intv || (intv->kind == kind));
      }
  
      if (!str_restr) {
          /* we do not have any restriction, return superior ones */
          *ret = intv;
          return EXIT_SUCCESS;
      }
  
      /* adjust local min and max */
      if (intv) {
          tmp_intv = intv;
  
          if (kind == 0) {
              local_umin = tmp_intv->value.uval.min;
          } else if (kind == 1) {
              local_smin = tmp_intv->value.sval.min;
          } else if (kind == 2) {
              local_fmin = tmp_intv->value.fval.min;
          }
  
          while (tmp_intv->next) {
              tmp_intv = tmp_intv->next;
          }
  
          if (kind == 0) {
              local_umax = tmp_intv->value.uval.max;
          } else if (kind == 1) {
              local_smax = tmp_intv->value.sval.max;
          } else if (kind == 2) {
              local_fmax = tmp_intv->value.fval.max;
          }
      }
  
      /* finally parse our restriction */
      seg_ptr = str_restr;
      tmp_intv = NULL;
      while (1) {
          if (!tmp_local_intv) {
              assert(!local_intv);
              local_intv = malloc(sizeof *local_intv);
              tmp_local_intv = local_intv;
          } else {
              tmp_local_intv->next = malloc(sizeof *tmp_local_intv);
              tmp_local_intv = tmp_local_intv->next;
          }
          LY_CHECK_ERR_GOTO(!tmp_local_intv, LOGMEM(ctx), error);
  
          tmp_local_intv->kind = kind;
          tmp_local_intv->type = type;
          tmp_local_intv->next = NULL;
  
          /* min */
          ptr = seg_ptr;
          while (isspace(ptr[0])) {
              ++ptr;
          }
          if (isdigit(ptr[0]) || (ptr[0] == '+') || (ptr[0] == '-')) {
              if (kind == 0) {
                  tmp_local_intv->value.uval.min = strtoull(ptr, (char **)&ptr, 10);
              } else if (kind == 1) {
                  tmp_local_intv->value.sval.min = strtoll(ptr, (char **)&ptr, 10);
              } else if (kind == 2) {
                  if (parse_range_dec64(&ptr, local_fdig, &tmp_local_intv->value.fval.min)) {
                      LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, ptr, "range");
                      goto error;
                  }
              }
          } else if (!strncmp(ptr, "min", 3)) {
              if (kind == 0) {
                  tmp_local_intv->value.uval.min = local_umin;
              } else if (kind == 1) {
                  tmp_local_intv->value.sval.min = local_smin;
              } else if (kind == 2) {
                  tmp_local_intv->value.fval.min = local_fmin;
              }
  
              ptr += 3;
          } else if (!strncmp(ptr, "max", 3)) {
              if (kind == 0) {
                  tmp_local_intv->value.uval.min = local_umax;
              } else if (kind == 1) {
                  tmp_local_intv->value.sval.min = local_smax;
              } else if (kind == 2) {
                  tmp_local_intv->value.fval.min = local_fmax;
              }
  
              ptr += 3;
          } else {
              goto error;
          }
  
          while (isspace(ptr[0])) {
              ptr++;
          }
  
          /* no interval or interval */
          if ((ptr[0] == '|') || !ptr[0]) {
              if (kind == 0) {
                  tmp_local_intv->value.uval.max = tmp_local_intv->value.uval.min;
              } else if (kind == 1) {
                  tmp_local_intv->value.sval.max = tmp_local_intv->value.sval.min;
              } else if (kind == 2) {
                  tmp_local_intv->value.fval.max = tmp_local_intv->value.fval.min;
              }
          } else if (!strncmp(ptr, "..", 2)) {
              /* skip ".." */
              ptr += 2;
              while (isspace(ptr[0])) {
                  ++ptr;
              }
  
              /* max */
              if (isdigit(ptr[0]) || (ptr[0] == '+') || (ptr[0] == '-')) {
                  if (kind == 0) {
                      tmp_local_intv->value.uval.max = strtoull(ptr, (char **)&ptr, 10);
                  } else if (kind == 1) {
                      tmp_local_intv->value.sval.max = strtoll(ptr, (char **)&ptr, 10);
                  } else if (kind == 2) {
                      if (parse_range_dec64(&ptr, local_fdig, &tmp_local_intv->value.fval.max)) {
                          LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, ptr, "range");
                          goto error;
                      }
                  }
              } else if (!strncmp(ptr, "max", 3)) {
                  if (kind == 0) {
                      tmp_local_intv->value.uval.max = local_umax;
                  } else if (kind == 1) {
                      tmp_local_intv->value.sval.max = local_smax;
                  } else if (kind == 2) {
                      tmp_local_intv->value.fval.max = local_fmax;
                  }
              } else {
                  goto error;
              }
          } else {
              goto error;
          }
  
          /* check min and max in correct order */
          if (kind == 0) {
              /* current segment */
              if (tmp_local_intv->value.uval.min > tmp_local_intv->value.uval.max) {
                  goto error;
              }
              if (tmp_local_intv->value.uval.min < local_umin || tmp_local_intv->value.uval.max > local_umax) {
                  goto error;
              }
              /* segments sholud be ascending order */
              if (tmp_intv && (tmp_intv->value.uval.max >= tmp_local_intv->value.uval.min)) {
                  goto error;
              }
          } else if (kind == 1) {
              if (tmp_local_intv->value.sval.min > tmp_local_intv->value.sval.max) {
                  goto error;
              }
              if (tmp_local_intv->value.sval.min < local_smin || tmp_local_intv->value.sval.max > local_smax) {
                  goto error;
              }
              if (tmp_intv && (tmp_intv->value.sval.max >= tmp_local_intv->value.sval.min)) {
                  goto error;
              }
          } else if (kind == 2) {
              if (tmp_local_intv->value.fval.min > tmp_local_intv->value.fval.max) {
                  goto error;
              }
              if (tmp_local_intv->value.fval.min < local_fmin || tmp_local_intv->value.fval.max > local_fmax) {
                  goto error;
              }
              if (tmp_intv && (tmp_intv->value.fval.max >= tmp_local_intv->value.fval.min)) {
                  /* fraction-digits value is always the same (it cannot be changed in derived types) */
                  goto error;
              }
          }
  
          /* next segment (next OR) */
          seg_ptr = strchr(seg_ptr, '|');
          if (!seg_ptr) {
              break;
          }
          seg_ptr++;
          tmp_intv = tmp_local_intv;
      }
  
      /* check local restrictions against superior ones */
      if (intv) {
          tmp_intv = intv;
          tmp_local_intv = local_intv;
  
          while (tmp_local_intv && tmp_intv) {
              /* reuse local variables */
              if (kind == 0) {
                  local_umin = tmp_local_intv->value.uval.min;
                  local_umax = tmp_local_intv->value.uval.max;
  
                  /* it must be in this interval */
                  if ((local_umin >= tmp_intv->value.uval.min) && (local_umin <= tmp_intv->value.uval.max)) {
                      /* this interval is covered, next one */
                      if (local_umax <= tmp_intv->value.uval.max) {
                          tmp_local_intv = tmp_local_intv->next;
                          continue;
                      /* ascending order of restrictions -> fail */
                      } else {
                          goto error;
                      }
                  }
              } else if (kind == 1) {
                  local_smin = tmp_local_intv->value.sval.min;
                  local_smax = tmp_local_intv->value.sval.max;
  
                  if ((local_smin >= tmp_intv->value.sval.min) && (local_smin <= tmp_intv->value.sval.max)) {
                      if (local_smax <= tmp_intv->value.sval.max) {
                          tmp_local_intv = tmp_local_intv->next;
                          continue;
                      } else {
                          goto error;
                      }
                  }
              } else if (kind == 2) {
                  local_fmin = tmp_local_intv->value.fval.min;
                  local_fmax = tmp_local_intv->value.fval.max;
  
                   if ((dec64cmp(local_fmin, local_fdig, tmp_intv->value.fval.min, local_fdig) > -1)
                          && (dec64cmp(local_fmin, local_fdig, tmp_intv->value.fval.max, local_fdig) < 1)) {
                      if (dec64cmp(local_fmax, local_fdig, tmp_intv->value.fval.max, local_fdig) < 1) {
                          tmp_local_intv = tmp_local_intv->next;
                          continue;
                      } else {
                          goto error;
                      }
                  }
              }
  
              tmp_intv = tmp_intv->next;
          }
  
          /* some interval left uncovered -> fail */
          if (tmp_local_intv) {
              goto error;
          }
      }
  
      /* append the local intervals to all the intervals of the superior types, return it all */
      if (intv) {
          for (tmp_intv = intv; tmp_intv->next; tmp_intv = tmp_intv->next);
          tmp_intv->next = local_intv;
      } else {
          intv = local_intv;
      }
      *ret = intv;
  
      return EXIT_SUCCESS;
  
  error:
      while (intv) {
          tmp_intv = intv->next;
          free(intv);
          intv = tmp_intv;
      }
      while (local_intv) {
          tmp_local_intv = local_intv->next;
          free(local_intv);
          local_intv = tmp_local_intv;
      }
  
      return -1;
  }
  
  static int
  resolve_superior_type_check(struct lys_type *type)
  {
      uint32_t i;
  
      if (type->base == LY_TYPE_DER) {
          /* check that the referenced typedef is resolved */
          return EXIT_FAILURE;
      } else if (type->base == LY_TYPE_UNION) {
          /* check that all union types are resolved */
          for (i = 0; i < type->info.uni.count; ++i) {
              if (resolve_superior_type_check(&type->info.uni.types[i])) {
                  return EXIT_FAILURE;
              }
          }
      } else if (type->base == LY_TYPE_LEAFREF) {
          /* check there is path in some derived type */
          while (!type->info.lref.path) {
              assert(type->der);
              type = &type->der->type;
          }
      }
  
      return EXIT_SUCCESS;
  }
  
  /**
   * @brief Resolve a typedef, return only resolved typedefs if derived. If leafref, it must be
   * resolved for this function to return it. Does not log.
   *
   * @param[in] name Typedef name.
   * @param[in] mod_name Typedef name module name.
   * @param[in] module Main module.
   * @param[in] parent Parent of the resolved type definition.
   * @param[out] ret Pointer to the resolved typedef. Can be NULL.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  int
  resolve_superior_type(const char *name, const char *mod_name, const struct lys_module *module,
                        const struct lys_node *parent, struct lys_tpdf **ret)
  {
      int i, j;
      struct lys_tpdf *tpdf, *match;
      int tpdf_size;
  
      if (!mod_name) {
          /* no prefix, try built-in types */
          for (i = 1; i < LY_DATA_TYPE_COUNT; i++) {
              if (!strcmp(ly_types[i]->name, name)) {
                  if (ret) {
                      *ret = ly_types[i];
                  }
                  return EXIT_SUCCESS;
              }
          }
      } else {
          if (!strcmp(mod_name, module->name)) {
              /* prefix refers to the current module, ignore it */
              mod_name = NULL;
          }
      }
  
      if (!mod_name && parent) {
          /* search in local typedefs */
          while (parent) {
              switch (parent->nodetype) {
              case LYS_CONTAINER:
                  tpdf_size = ((struct lys_node_container *)parent)->tpdf_size;
                  tpdf = ((struct lys_node_container *)parent)->tpdf;
                  break;
  
              case LYS_LIST:
                  tpdf_size = ((struct lys_node_list *)parent)->tpdf_size;
                  tpdf = ((struct lys_node_list *)parent)->tpdf;
                  break;
  
              case LYS_GROUPING:
                  tpdf_size = ((struct lys_node_grp *)parent)->tpdf_size;
                  tpdf = ((struct lys_node_grp *)parent)->tpdf;
                  break;
  
              case LYS_RPC:
              case LYS_ACTION:
                  tpdf_size = ((struct lys_node_rpc_action *)parent)->tpdf_size;
                  tpdf = ((struct lys_node_rpc_action *)parent)->tpdf;
                  break;
  
              case LYS_NOTIF:
                  tpdf_size = ((struct lys_node_notif *)parent)->tpdf_size;
                  tpdf = ((struct lys_node_notif *)parent)->tpdf;
                  break;
  
              case LYS_INPUT:
              case LYS_OUTPUT:
                  tpdf_size = ((struct lys_node_inout *)parent)->tpdf_size;
                  tpdf = ((struct lys_node_inout *)parent)->tpdf;
                  break;
  
              default:
                  parent = lys_parent(parent);
                  continue;
              }
  
              for (i = 0; i < tpdf_size; i++) {
                  if (!strcmp(tpdf[i].name, name)) {
                      match = &tpdf[i];
                      goto check_typedef;
                  }
              }
  
              parent = lys_parent(parent);
          }
      } else {
          /* get module where to search */
          module = lyp_get_module(module, NULL, 0, mod_name, 0, 0);
          if (!module) {
              return -1;
          }
      }
  
      /* search in top level typedefs */
      for (i = 0; i < module->tpdf_size; i++) {
          if (!strcmp(module->tpdf[i].name, name)) {
              match = &module->tpdf[i];
              goto check_typedef;
          }
      }
  
      /* search in submodules */
      for (i = 0; i < module->inc_size && module->inc[i].submodule; i++) {
          for (j = 0; j < module->inc[i].submodule->tpdf_size; j++) {
              if (!strcmp(module->inc[i].submodule->tpdf[j].name, name)) {
                  match = &module->inc[i].submodule->tpdf[j];
                  goto check_typedef;
              }
          }
      }
  
      return EXIT_FAILURE;
  
  check_typedef:
      if (resolve_superior_type_check(&match->type)) {
          return EXIT_FAILURE;
      }
  
      if (ret) {
          *ret = match;
      }
      return EXIT_SUCCESS;
  }
  
  /**
   * @brief Check the default \p value of the \p type. Logs directly.
   *
   * @param[in] type Type definition to use.
   * @param[in] value Default value to check.
   * @param[in] module Type module.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  static int
  check_default(struct lys_type *type, const char **value, struct lys_module *module, int tpdf)
  {
      struct lys_tpdf *base_tpdf = NULL;
      struct lyd_node_leaf_list node;
      const char *dflt = NULL;
      char *s;
      int ret = EXIT_SUCCESS, r;
      struct ly_ctx *ctx = module->ctx;
  
      assert(value);
      memset(&node, 0, sizeof node);
  
      if (type->base <= LY_TYPE_DER) {
          /* the type was not resolved yet, nothing to do for now */
          ret = EXIT_FAILURE;
          goto cleanup;
      } else if (!module->implemented && ((type->base == LY_TYPE_IDENT) || (type->base == LY_TYPE_INST))) {
          /* /instidsidentityrefs are checked when instantiated in data instead of typedef,
           * but the value has to be modified to include the prefix */
          if (*value) {
              if (strchr(*value, ':')) {
                  dflt = transform_schema2json(module, *value);
              } else {
                  /* default prefix of the module where the typedef is defined */
                  if (asprintf(&s, "%s:%s", lys_main_module(module)->name, *value) == -1) {
                      LOGMEM(ctx);
                      ret = -1;
                      goto cleanup;
                  }
                  dflt = lydict_insert_zc(ctx, s);
              }
              lydict_remove(ctx, *value);
              *value = dflt;
              dflt = NULL;
          }
          goto cleanup;
      } else if (type->base == LY_TYPE_LEAFREF && tpdf) {
          /* leafref in typedef cannot be checked */
          goto cleanup;
      }
  
      dflt = lydict_insert(ctx, *value, 0);
      if (!dflt) {
          /* we do not have a new default value, so is there any to check even, in some base type? */
          for (base_tpdf = type->der; base_tpdf->type.der; base_tpdf = base_tpdf->type.der) {
              if (base_tpdf->dflt) {
                  dflt = lydict_insert(ctx, base_tpdf->dflt, 0);
                  break;
              }
          }
  
          if (!dflt) {
              /* no default value, nothing to check, all is well */
              goto cleanup;
          }
  
          /* so there is a default value in a base type, but can the default value be no longer valid (did we define some new restrictions)? */
          switch (type->base) {
          case LY_TYPE_IDENT:
              if (lys_main_module(base_tpdf->type.parent->module)->implemented) {
                  goto cleanup;
              } else if ((type->base == LY_TYPE_IDENT) || (type->base == LY_TYPE_INST)) {
                  /* impossible to check with a non-implemented module */
                  goto cleanup;
              } else {
                  /* check the default value from typedef, but use also the typedef's module
                   * due to possible searching in imported modules which is expected in
                   * typedef's module instead of module where the typedef is used */
                  module = base_tpdf->module;
              }
              break;
          case LY_TYPE_INST:
          case LY_TYPE_LEAFREF:
          case LY_TYPE_BOOL:
          case LY_TYPE_EMPTY:
              /* these have no restrictions, so we would do the exact same work as the unres in the base typedef */
              goto cleanup;
          case LY_TYPE_BITS:
              /* the default value must match the restricted list of values, if the type was restricted */
              if (type->info.bits.count) {
                  break;
              }
              goto cleanup;
          case LY_TYPE_ENUM:
              /* the default value must match the restricted list of values, if the type was restricted */
              if (type->info.enums.count) {
                  break;
              }
              goto cleanup;
          case LY_TYPE_DEC64:
              if (type->info.dec64.range) {
                  break;
              }
              goto cleanup;
          case LY_TYPE_BINARY:
              if (type->info.binary.length) {
                  break;
              }
              goto cleanup;
          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:
              if (type->info.num.range) {
                  break;
              }
              goto cleanup;
          case LY_TYPE_STRING:
              if (type->info.str.length || type->info.str.patterns) {
                  break;
              }
              goto cleanup;
          case LY_TYPE_UNION:
              /* way too much trouble learning whether we need to check the default again, so just do it */
              break;
          default:
              LOGINT(ctx);
              ret = -1;
              goto cleanup;
          }
      } else if (type->base == LY_TYPE_EMPTY) {
          LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_NONE, NULL, "default", type->parent->name);
          LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "The \"empty\" data type cannot have a default value.");
          ret = -1;
          goto cleanup;
      }
  
      /* dummy leaf */
      memset(&node, 0, sizeof node);
      node.value_str = lydict_insert(ctx, dflt, 0);
      node.value_type = type->base;
  
      if (tpdf) {
          node.schema = calloc(1, sizeof (struct lys_node_leaf));
          if (!node.schema) {
              LOGMEM(ctx);
              ret = -1;
              goto cleanup;
          }
          r = asprintf((char **)&node.schema->name, "typedef-%s-default", ((struct lys_tpdf *)type->parent)->name);
          if (r == -1) {
              LOGMEM(ctx);
              ret = -1;
              goto cleanup;
          }
          node.schema->module = module;
          memcpy(&((struct lys_node_leaf *)node.schema)->type, type, sizeof *type);
      } else {
          node.schema = (struct lys_node *)type->parent;
      }
  
      if (type->base == LY_TYPE_LEAFREF) {
          if (!type->info.lref.target) {
              ret = EXIT_FAILURE;
              LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Default value \"%s\" cannot be checked in an unresolved leafref.",
                     dflt);
              goto cleanup;
          }
          ret = check_default(&type->info.lref.target->type, &dflt, module, 0);
          if (!ret) {
              /* adopt possibly changed default value to its canonical form */
              if (*value) {
                  lydict_remove(ctx, *value);
                  *value = dflt;
                  dflt = NULL;
              }
          }
      } else {
          if (!lyp_parse_value(type, &node.value_str, NULL, &node, NULL, module, 1, 1)) {
              /* possible forward reference */
              ret = EXIT_FAILURE;
              if (base_tpdf) {
                  /* default value is defined in some base typedef */
                  if ((type->base == LY_TYPE_BITS && type->der->type.der) ||
                          (type->base == LY_TYPE_ENUM && type->der->type.der)) {
                      /* we have refined bits/enums */
                      LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL,
                             "Invalid value \"%s\" of the default statement inherited to \"%s\" from \"%s\" base type.",
                             dflt, type->parent->name, base_tpdf->name);
                  }
              }
          } else {
              /* success - adopt canonical form from the node into the default value */
              if (!ly_strequal(dflt, node.value_str, 1)) {
                  /* this can happen only if we have non-inherited default value,
                   * inherited default values are already in canonical form */
                  assert(ly_strequal(dflt, *value, 1));
  
                  lydict_remove(ctx, *value);
                  *value = node.value_str;
                  node.value_str = NULL;
              }
          }
      }
  
  cleanup:
      lyd_free_value(node.value, node.value_type, node.value_flags, type, node.value_str, NULL, NULL, NULL);
      lydict_remove(ctx, node.value_str);
      if (tpdf && node.schema) {
          free((char *)node.schema->name);
          free(node.schema);
      }
      lydict_remove(ctx, dflt);
  
      return ret;
  }
  
  /**
   * @brief Check a key for mandatory attributes. Logs directly.
   *
   * @param[in] key The key to check.
   * @param[in] flags What flags to check.
   * @param[in] list The list of all the keys.
   * @param[in] index Index of the key in the key list.
   * @param[in] name The name of the keys.
   * @param[in] len The name length.
   *
   * @return EXIT_SUCCESS on success, -1 on error.
   */
  static int
  check_key(struct lys_node_list *list, int index, const char *name, int len)
  {
      struct lys_node_leaf *key = list->keys[index];
      char *dup = NULL;
      int j;
      struct ly_ctx *ctx = list->module->ctx;
  
      /* existence */
      if (!key) {
          if (name[len] != '\0') {
              dup = strdup(name);
              LY_CHECK_ERR_RETURN(!dup, LOGMEM(ctx), -1);
              dup[len] = '\0';
              name = dup;
          }
          LOGVAL(ctx, LYE_KEY_MISS, LY_VLOG_LYS, list, name);
          free(dup);
          return -1;
      }
  
      /* uniqueness */
      for (j = index - 1; j >= 0; j--) {
          if (key == list->keys[j]) {
              LOGVAL(ctx, LYE_KEY_DUP, LY_VLOG_LYS, list, key->name);
              return -1;
          }
      }
  
      /* key is a leaf */
      if (key->nodetype != LYS_LEAF) {
          LOGVAL(ctx, LYE_KEY_NLEAF, LY_VLOG_LYS, list, key->name);
          return -1;
      }
  
      /* type of the leaf is not built-in empty */
      if (key->type.base == LY_TYPE_EMPTY && key->module->version < LYS_VERSION_1_1) {
          LOGVAL(ctx, LYE_KEY_TYPE, LY_VLOG_LYS, list, key->name);
          return -1;
      }
  
      /* config attribute is the same as of the list */
      if ((key->flags & LYS_CONFIG_MASK) && (list->flags & LYS_CONFIG_MASK)
              && ((list->flags & LYS_CONFIG_MASK) != (key->flags & LYS_CONFIG_MASK))) {
          LOGVAL(ctx, LYE_KEY_CONFIG, LY_VLOG_LYS, list, key->name);
          return -1;
      }
  
      /* key is not placed from augment */
      if (key->parent->nodetype == LYS_AUGMENT) {
          LOGVAL(ctx, LYE_KEY_MISS, LY_VLOG_LYS, key, key->name);
          LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Key inserted from augment.");
          return -1;
      }
  
      /* key is not when/if-feature -conditional
       * note that j is really assigned in condition to distinguish when and if-feature, it is not supposed to be a comparison */
      j = 0;
      if (key->when || (key->iffeature_size && (j = 1))) {
          LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_LYS, key, j ? "if-feature" : "when", "leaf");
          LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Key definition cannot depend on a \"%s\" condition.",
                 j ? "if-feature" : "when");
          return -1;
      }
  
      return EXIT_SUCCESS;
  }
  
  /**
   * @brief Resolve (test the target exists) unique. Logs directly.
   *
   * @param[in] parent The parent node of the unique structure.
   * @param[in] uniq_str_path One path from the unique string.
   *
   * @return EXIT_SUCCESS on succes, EXIT_FAILURE on forward reference, -1 on error.
   */
  int
  resolve_unique(struct lys_node *parent, const char *uniq_str_path, uint8_t *trg_type)
  {
      int rc;
      const struct lys_node *leaf = NULL;
      struct ly_ctx *ctx = parent->module->ctx;
  
      rc = resolve_descendant_schema_nodeid(uniq_str_path, *lys_child(parent, LYS_LEAF), LYS_LEAF, 1, &leaf);
      if (rc || !leaf) {
          if (rc) {
              LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, parent, uniq_str_path, "unique");
              if (rc > 0) {
                  LOGVAL(ctx, LYE_INCHAR, LY_VLOG_PREV, NULL, uniq_str_path[rc - 1], &uniq_str_path[rc - 1]);
              } else if (rc == -2) {
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Unique argument references list.");
              }
              rc = -1;
          } else {
              LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, parent, uniq_str_path, "unique");
              LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Target leaf not found.");
              rc = EXIT_FAILURE;
          }
          goto error;
      }
      if (leaf->nodetype != LYS_LEAF) {
          LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, parent, uniq_str_path, "unique");
          LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Target is not a leaf.");
          return -1;
      }
  
      /* check status */
      if (parent->nodetype != LYS_EXT && lyp_check_status(parent->flags, parent->module, parent->name,
                                                          leaf->flags, leaf->module, leaf->name, leaf)) {
          return -1;
      }
  
      /* check that all unique's targets are of the same config type */
      if (*trg_type) {
          if (((*trg_type == 1) && (leaf->flags & LYS_CONFIG_R)) || ((*trg_type == 2) && (leaf->flags & LYS_CONFIG_W))) {
              LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, parent, uniq_str_path, "unique");
              LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL,
                     "Leaf \"%s\" referenced in unique statement is config %s, but previous referenced leaf is config %s.",
                     uniq_str_path, *trg_type == 1 ? "false" : "true", *trg_type == 1 ? "true" : "false");
              return -1;
          }
      } else {
          /* first unique */
          if (leaf->flags & LYS_CONFIG_W) {
              *trg_type = 1;
          } else {
              *trg_type = 2;
          }
      }
  
      /* set leaf's unique flag */
      ((struct lys_node_leaf *)leaf)->flags |= LYS_UNIQUE;
  
      return EXIT_SUCCESS;
  
  error:
  
      return rc;
  }
  
  void
  unres_data_del(struct unres_data *unres, uint32_t i)
  {
      /* there are items after the one deleted */
      if (i+1 < unres->count) {
          /* we only move the data, memory is left allocated, why bother */
          memmove(&unres->node[i], &unres->node[i+1], (unres->count-(i+1)) * sizeof *unres->node);
  
      /* deleting the last item */
      } else if (i == 0) {
          free(unres->node);
          unres->node = NULL;
      }
  
      /* if there are no items after and it is not the last one, just move the counter */
      --unres->count;
  }
  
  /**
   * @brief Resolve (find) a data node from a specific module. Does not log.
   *
   * @param[in] mod Module to search in.
   * @param[in] name Name of the data node.
   * @param[in] nam_len Length of the name.
   * @param[in] start Data node to start the search from.
   * @param[in,out] parents Resolved nodes. If there are some parents,
   *                        they are replaced (!!) with the resolvents.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  static int
  resolve_data(const struct lys_module *mod, const char *name, int nam_len, struct lyd_node *start, struct unres_data *parents)
  {
      struct lyd_node *node;
      int flag;
      uint32_t i;
  
      if (!parents->count) {
          parents->count = 1;
          parents->node = malloc(sizeof *parents->node);
          LY_CHECK_ERR_RETURN(!parents->node, LOGMEM(mod->ctx), -1);
          parents->node[0] = NULL;
      }
      for (i = 0; i < parents->count;) {
          if (parents->node[i] && (parents->node[i]->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) {
              /* skip */
              ++i;
              continue;
          }
          flag = 0;
          LY_TREE_FOR(parents->node[i] ? parents->node[i]->child : start, node) {
              if (lyd_node_module(node) == mod && !strncmp(node->schema->name, name, nam_len)
                      && node->schema->name[nam_len] == '\0') {
                  /* matching target */
                  if (!flag) {
                      /* put node instead of the current parent */
                      parents->node[i] = node;
                      flag = 1;
                  } else {
                      /* multiple matching, so create a new node */
                      ++parents->count;
                      parents->node = ly_realloc(parents->node, parents->count * sizeof *parents->node);
                      LY_CHECK_ERR_RETURN(!parents->node, LOGMEM(mod->ctx), EXIT_FAILURE);
                      parents->node[parents->count-1] = node;
                      ++i;
                  }
              }
          }
  
          if (!flag) {
              /* remove item from the parents list */
              unres_data_del(parents, i);
          } else {
              ++i;
          }
      }
  
      return parents->count ? EXIT_SUCCESS : EXIT_FAILURE;
  }
  
  static int
  resolve_schema_leafref_valid_dep_flag(const struct lys_node *op_node, const struct lys_module *local_mod,
                                        const struct lys_node *first_node, int abs_path)
  {
      const struct lys_node *node;
  
      if (!op_node) {
          /* leafref pointing to a different module */
          if (local_mod != lys_node_module(first_node)) {
              return 1;
          }
      } else if (lys_parent(op_node)) {
          /* inner operation (notif/action) */
          if (abs_path) {
              return 1;
          } else {
              /* if first_node parent is not op_node, it is external */
              for (node = first_node; node && (node != op_node); node = lys_parent(node));
              if (!node) {
                  return 1;
              }
          }
      } else {
          /* top-level operation (notif/rpc) */
          if (op_node != first_node) {
              return 1;
          }
      }
  
      return 0;
  }
  
  /**
   * @brief Resolve a path (leafref) predicate in JSON schema context. Logs directly.
   *
   * @param[in] path Path to use.
   * @param[in] context_node Predicate context node (where the predicate is placed).
   * @param[in] parent Path context node (where the path begins/is placed).
   * @param[in] node_set Set where to add nodes whose parent chain must be implemented.
   *
   * @return 0 on forward reference, otherwise the number
   *         of characters successfully parsed,
   *         positive on success, negative on failure.
   */
  static int
  resolve_schema_leafref_predicate(const char *path, const struct lys_node *context_node, struct lys_node *parent,
                                   struct ly_set *node_set)
  {
      const struct lys_module *trg_mod;
      const struct lys_node *src_node, *dst_node, *tmp_parent;
      struct lys_node_augment *last_aug;
      const char *path_key_expr, *source, *sour_pref, *dest, *dest_pref;
      int pke_len, sour_len, sour_pref_len, dest_len, dest_pref_len, pke_parsed, parsed = 0;
      int has_predicate, dest_parent_times, i, rc;
      struct ly_ctx *ctx = context_node->module->ctx;
  
      do {
          if ((i = parse_path_predicate(path, &sour_pref, &sour_pref_len, &source, &sour_len, &path_key_expr,
                                        &pke_len, &has_predicate)) < 1) {
              LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYS, parent, path[-i], path - i);
              return -parsed + i;
          }
          parsed += i;
          path += i;
  
          /* source (must be leaf) */
          if (sour_pref) {
              trg_mod = lyp_get_module(lys_node_module(parent), NULL, 0, sour_pref, sour_pref_len, 0);
          } else {
              trg_mod = lys_node_module(parent);
          }
          rc = lys_getnext_data(trg_mod, context_node, source, sour_len, LYS_LEAF | LYS_LEAFLIST, LYS_GETNEXT_NOSTATECHECK,
                                &src_node);
          if (rc) {
              LOGVAL(ctx, LYE_NORESOLV, LY_VLOG_LYS, parent, "leafref predicate", path-parsed);
              return 0;
          }
  
          /* destination */
          dest_parent_times = 0;
          pke_parsed = 0;
          if ((i = parse_path_key_expr(path_key_expr, &dest_pref, &dest_pref_len, &dest, &dest_len,
                                       &dest_parent_times)) < 1) {
              LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYS, parent, path_key_expr[-i], path_key_expr-i);
              return -parsed;
          }
          pke_parsed += i;
  
          for (i = 0, dst_node = parent; i < dest_parent_times; ++i) {
              if (!dst_node) {
                  /* we went too much into parents, there is no parent anymore */
                  LOGVAL(ctx, LYE_NORESOLV, LY_VLOG_LYS, parent, "leafref predicate", path_key_expr);
                  return 0;
              }
  
              if (dst_node->parent && (dst_node->parent->nodetype == LYS_AUGMENT)
                      && !((struct lys_node_augment *)dst_node->parent)->target) {
                  /* we are in an unresolved augment, cannot evaluate */
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, dst_node->parent,
                         "Cannot resolve leafref predicate \"%s\" because it is in an unresolved augment.", path_key_expr);
                  return 0;
              }
  
              /* path is supposed to be evaluated in data tree, so we have to skip
               * all schema nodes that cannot be instantiated in data tree */
              for (dst_node = lys_parent(dst_node);
                   dst_node && !(dst_node->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_ACTION | LYS_NOTIF | LYS_RPC));
                   dst_node = lys_parent(dst_node));
          }
          while (1) {
              last_aug = NULL;
  
              if (dest_pref) {
                  trg_mod = lyp_get_module(lys_node_module(parent), NULL, 0, dest_pref, dest_pref_len, 0);
              } else {
                  trg_mod = lys_node_module(parent);
              }
  
              if (!trg_mod->implemented && dst_node) {
      get_next_augment:
                  last_aug = lys_getnext_target_aug(last_aug, trg_mod, dst_node);
              }
  
              tmp_parent = (last_aug ? (struct lys_node *)last_aug : dst_node);
              rc = lys_getnext_data(trg_mod, tmp_parent, dest, dest_len, LYS_CONTAINER | LYS_LIST | LYS_LEAF,
                                    LYS_GETNEXT_NOSTATECHECK, &dst_node);
              if (rc) {
                  if (last_aug) {
                      /* restore the correct augment target */
                      dst_node = last_aug->target;
                      goto get_next_augment;
                  }
                  LOGVAL(ctx, LYE_NORESOLV, LY_VLOG_LYS, parent, "leafref predicate", path_key_expr);
                  return 0;
              }
  
              if (pke_len == pke_parsed) {
                  break;
              }
  
              if ((i = parse_path_key_expr(path_key_expr + pke_parsed, &dest_pref, &dest_pref_len, &dest, &dest_len,
                                           &dest_parent_times)) < 1) {
                  LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYS, parent,
                         (path_key_expr + pke_parsed)[-i], (path_key_expr + pke_parsed) - i);
                  return -parsed;
              }
              pke_parsed += i;
          }
  
          /* check source - dest match */
          if (dst_node->nodetype != src_node->nodetype) {
              LOGVAL(ctx, LYE_NORESOLV, LY_VLOG_LYS, parent, "leafref predicate", path - parsed);
              LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Destination node is not a %s, but a %s.",
                     strnodetype(src_node->nodetype), strnodetype(dst_node->nodetype));
              return -parsed;
          }
  
          /* add both nodes into node set */
          ly_set_add(node_set, (void *)dst_node, 0);
          ly_set_add(node_set, (void *)src_node, 0);
      } while (has_predicate);
  
      return parsed;
  }
  
  static int
  check_leafref_features(struct lys_type *type)
  {
      struct lys_node *iter;
      struct ly_set *src_parents, *trg_parents, *features;
      struct lys_node_augment *aug;
      struct ly_ctx *ctx = ((struct lys_tpdf *)type->parent)->module->ctx;
      unsigned int i, j, size, x;
      int ret = EXIT_SUCCESS;
  
      assert(type->parent);
  
      src_parents = ly_set_new();
      trg_parents = ly_set_new();
      features = ly_set_new();
  
      /* get parents chain of source (leafref) */
      for (iter = (struct lys_node *)type->parent; iter; iter = lys_parent(iter)) {
          if (iter->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
              continue;
          }
          if (iter->parent && (iter->parent->nodetype == LYS_AUGMENT)) {
              aug = (struct lys_node_augment *)iter->parent;
              if ((aug->module->implemented && (aug->flags & LYS_NOTAPPLIED)) || !aug->target) {
                  /* unresolved augment, wait until it's resolved */
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, aug,
                         "Cannot check leafref \"%s\" if-feature consistency because of an unresolved augment.", type->info.lref.path);
                  ret = EXIT_FAILURE;
                  goto cleanup;
              }
              /* also add this augment */
              ly_set_add(src_parents, aug, LY_SET_OPT_USEASLIST);
          }
          ly_set_add(src_parents, iter, LY_SET_OPT_USEASLIST);
      }
      /* get parents chain of target */
      for (iter = (struct lys_node *)type->info.lref.target; iter; iter = lys_parent(iter)) {
          if (iter->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
              continue;
          }
          if (iter->parent && (iter->parent->nodetype == LYS_AUGMENT)) {
              aug = (struct lys_node_augment *)iter->parent;
              if ((aug->module->implemented && (aug->flags & LYS_NOTAPPLIED)) || !aug->target) {
                  /* unresolved augment, wait until it's resolved */
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, aug,
                         "Cannot check leafref \"%s\" if-feature consistency because of an unresolved augment.", type->info.lref.path);
                  ret = EXIT_FAILURE;
                  goto cleanup;
              }
          }
          ly_set_add(trg_parents, iter, LY_SET_OPT_USEASLIST);
      }
  
      /* compare the features used in if-feature statements in the rest of both
       * chains of parents. The set of features used for target must be a subset
       * of features used for the leafref. This is not a perfect, we should compare
       * the truth tables but it could require too much resources, so we simplify that */
      for (i = 0; i < src_parents->number; i++) {
          iter = src_parents->set.s[i]; /* shortcut */
          if (!iter->iffeature_size) {
              continue;
          }
          for (j = 0; j < iter->iffeature_size; j++) {
              resolve_iffeature_getsizes(&iter->iffeature[j], NULL, &size);
              for (; size; size--) {
                  if (!iter->iffeature[j].features[size - 1]) {
                      /* not yet resolved feature, postpone this check */
                      ret = EXIT_FAILURE;
                      goto cleanup;
                  }
                  ly_set_add(features, iter->iffeature[j].features[size - 1], 0);
              }
          }
      }
      x = features->number;
      for (i = 0; i < trg_parents->number; i++) {
          iter = trg_parents->set.s[i]; /* shortcut */
          if (!iter->iffeature_size) {
              continue;
          }
          for (j = 0; j < iter->iffeature_size; j++) {
              resolve_iffeature_getsizes(&iter->iffeature[j], NULL, &size);
              for (; size; size--) {
                  if (!iter->iffeature[j].features[size - 1]) {
                      /* not yet resolved feature, postpone this check */
                      ret = EXIT_FAILURE;
                      goto cleanup;
                  }
                  if ((unsigned)ly_set_add(features, iter->iffeature[j].features[size - 1], 0) >= x) {
                      /* the feature is not present in features set of target's parents chain */
                      LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, type->parent,
                             "Leafref is not conditional based on \"%s\" feature as its target.",
                             iter->iffeature[j].features[size - 1]->name);
                      for (iter = type->info.lref.target->parent; iter && (iter->nodetype != LYS_USES); iter = lys_parent(iter));
                      if (iter) {
                          /* we are in a uses so there can still be a refine that will add an if-feature */
                          ret = EXIT_FAILURE;
                      } else {
                          ret = -1;
                      }
                      goto cleanup;
                  }
              }
          }
      }
  
  cleanup:
      ly_set_free(features);
      ly_set_free(src_parents);
      ly_set_free(trg_parents);
  
      return ret;
  }
  
  /**
   * @brief Resolve a path (leafref) in JSON schema context. Logs directly.
   *
   * @param[in] path Path to use.
   * @param[in] parent_node Parent of the leafref.
   * @param[out] ret Pointer to the resolved schema node. Can be NULL.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  static int
  resolve_schema_leafref(struct lys_type *type, struct lys_node *parent, struct unres_schema *unres)
  {
      const struct lys_node *node, *op_node = NULL, *tmp_parent;
      struct ly_set *node_set;
      struct lys_node_augment *last_aug;
      const struct lys_module *tmp_mod, *cur_module;
      const char *id, *prefix, *name;
      int pref_len, nam_len, parent_times, has_predicate;
      int i, first_iter;
      struct ly_ctx *ctx = parent->module->ctx;
  
      /* first check that we are not in an unresolved augment */
      for (node = parent; lys_parent(node); node = lys_parent(node));
      if (node->parent && (node->parent->nodetype == LYS_AUGMENT)) {
          LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, node->parent,
                 "Cannot resolve leafref \"%s\" because it is in an unresolved augment.", type->info.lref.path);
          return EXIT_FAILURE;
      }
  
      first_iter = 1;
      parent_times = 0;
      id = type->info.lref.path;
      node_set = ly_set_new();
      if (!node_set) {
          LOGMEM(ctx);
          return -1;
      }
  
      /* find operation schema we are in */
      for (op_node = lys_parent(parent);
          op_node && !(op_node->nodetype & (LYS_ACTION | LYS_NOTIF | LYS_RPC));
          op_node = lys_parent(op_node));
  
      if (type->der->module) {
          /* typedef, take its local module */
          cur_module = type->der->module;
      } else {
          cur_module = lys_node_module(parent);
      }
      do {
          if ((i = parse_path_arg(cur_module, id, &prefix, &pref_len, &name, &nam_len, &parent_times, &has_predicate)) < 1) {
              LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYS, parent, id[-i], &id[-i]);
              ly_set_free(node_set);
              return -1;
          }
          id += i;
  
          /* get the current module */
          tmp_mod = prefix ? lyp_get_module(cur_module, NULL, 0, prefix, pref_len, 0) : cur_module;
          if (!tmp_mod) {
              LOGVAL(ctx, LYE_NORESOLV, LY_VLOG_LYS, parent, "leafref", type->info.lref.path);
              ly_set_free(node_set);
              return EXIT_FAILURE;
          }
          last_aug = NULL;
  
          if (first_iter) {
              if (parent_times == -1) {
                  /* use module data */
                  node = NULL;
  
              } else if (parent_times > 0) {
                  /* we are looking for the right parent */
                  for (i = 0, node = parent; i < parent_times; i++) {
                      /* path is supposed to be evaluated in data tree, so we have to skip
                       * all schema nodes that cannot be instantiated in data tree */
                      for (node = lys_parent(node);
                          node && !(node->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_ACTION | LYS_NOTIF | LYS_RPC));
                          node = lys_parent(node));
  
                      if (!node) {
                          if (i == parent_times - 1) {
                              /* top-level */
                              break;
                          }
  
                          /* higher than top-level */
                          LOGVAL(ctx, LYE_NORESOLV, LY_VLOG_LYS, parent, "leafref", type->info.lref.path);
                          ly_set_free(node_set);
                          return EXIT_FAILURE;
                      }
                  }
              } else {
                  LOGINT(ctx);
                  ly_set_free(node_set);
                  return -1;
              }
          }
  
          /* find the next node (either in unconnected augment or as a schema sibling, node is NULL for top-level node -
           * - useless to search for that in augments) */
          if (!tmp_mod->implemented && node) {
      get_next_augment:
              last_aug = lys_getnext_target_aug(last_aug, tmp_mod, node);
          }
  
          tmp_parent = (last_aug ? (struct lys_node *)last_aug : node);
          node = NULL;
          while ((node = lys_getnext(node, tmp_parent, tmp_mod, LYS_GETNEXT_NOSTATECHECK))) {
              if (lys_node_module(node) != lys_main_module(tmp_mod)) {
                  continue;
              }
              if (strncmp(node->name, name, nam_len) || node->name[nam_len]) {
                  continue;
              }
              /* match */
              break;
          }
          if (!node) {
              if (last_aug) {
                  /* restore the correct augment target */
                  node = last_aug->target;
                  goto get_next_augment;
              }
              LOGVAL(ctx, LYE_NORESOLV, LY_VLOG_LYS, parent, "leafref", type->info.lref.path);
              ly_set_free(node_set);
              return EXIT_FAILURE;
          }
  
          if (first_iter) {
              /* find module whose data will actually contain this leafref */
              for (tmp_parent = parent; lys_parent(tmp_parent); tmp_parent = lys_parent(tmp_parent));
  
              /* set external dependency flag, we can decide based on the first found node */
              if (resolve_schema_leafref_valid_dep_flag(op_node, lys_node_module(tmp_parent), node, (parent_times == -1 ? 1 : 0))) {
                  parent->flags |= LYS_LEAFREF_DEP;
              }
              first_iter = 0;
          }
  
          if (has_predicate) {
              /* we have predicate, so the current result must be list */
              if (node->nodetype != LYS_LIST) {
                  LOGVAL(ctx, LYE_NORESOLV, LY_VLOG_LYS, parent, "leafref", type->info.lref.path);
                  ly_set_free(node_set);
                  return -1;
              }
  
              i = resolve_schema_leafref_predicate(id, node, parent, node_set);
              if (!i) {
                  ly_set_free(node_set);
                  return EXIT_FAILURE;
              } else if (i < 0) {
                  ly_set_free(node_set);
                  return -1;
              }
              id += i;
              has_predicate = 0;
          }
      } while (id[0]);
  
      /* the target must be leaf or leaf-list (in YANG 1.1 only) */
      if ((node->nodetype != LYS_LEAF) && (node->nodetype != LYS_LEAFLIST)) {
          LOGVAL(ctx, LYE_NORESOLV, LY_VLOG_LYS, parent, "leafref", type->info.lref.path);
          LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Leafref target \"%s\" is not a leaf nor a leaf-list.", type->info.lref.path);
          ly_set_free(node_set);
          return -1;
      }
  
      /* check status */
      if (lyp_check_status(parent->flags, parent->module, parent->name,
                      node->flags, node->module, node->name, node)) {
          ly_set_free(node_set);
          return -1;
      }
  
      /* assign */
      type->info.lref.target = (struct lys_node_leaf *)node;
  
      /* add the target node into a set so its parent chain modules can be implemented */
      ly_set_add(node_set, (void *)node, 0);
  
      /* find the actual module of this leafref, it can be in a foreign augment */
      for (node = parent; lys_parent(node); node = lys_parent(node));
      if (lys_node_module(node)->implemented) {
          /* make all the modules in the path implemented */
          for (i = 0; (unsigned)i < node_set->number; ++i) {
              for (node = node_set->set.s[i]; node; node = lys_parent(node)) {
                  if (!lys_node_module(node)->implemented) {
                      lys_node_module(node)->implemented = 1;
                      if (unres_schema_add_node(lys_node_module(node), unres, NULL, UNRES_MOD_IMPLEMENT, NULL) == -1) {
                          ly_set_free(node_set);
                          return -1;
                      }
                  }
              }
  
              /* check leafref that it is allowed */
              if (lys_leaf_check_leafref((struct lys_node_leaf *)node_set->set.s[i], (struct lys_node *)type->parent)) {
                  ly_set_free(node_set);
                  return -1;
              }
          }
      }
      ly_set_free(node_set);
  
      /* check if leafref and its target are under common if-features */
      return check_leafref_features(type);
  }
  
  /**
   * @brief Compare 2 data node values.
   *
   * Comparison performed on canonical forms, the first value
   * is first transformed into canonical form.
   *
   * @param[in] node Leaf/leaf-list with these values.
   * @param[in] noncan_val Non-canonical value.
   * @param[in] noncan_val_len Length of \p noncal_val.
   * @param[in] can_val Canonical value.
   * @return 1 if equal, 0 if not, -1 on error (logged).
   */
  static int
  valequal(struct lys_node *node, const char *noncan_val, int noncan_val_len, const char *can_val)
  {
      int ret;
      struct lyd_node_leaf_list leaf;
      struct lys_node_leaf *sleaf = (struct lys_node_leaf*)node;
  
      /* dummy leaf */
      memset(&leaf, 0, sizeof leaf);
      leaf.value_str = lydict_insert(node->module->ctx, noncan_val, noncan_val_len);
  
  repeat:
      leaf.value_type = sleaf->type.base;
      leaf.schema = node;
  
      if (leaf.value_type == LY_TYPE_LEAFREF) {
          if (!sleaf->type.info.lref.target) {
              /* it should either be unresolved leafref (leaf.value_type are ORed flags) or it will be resolved */
              LOGINT(node->module->ctx);
              ret = -1;
              goto finish;
          }
          sleaf = sleaf->type.info.lref.target;
          goto repeat;
      } else {
          if (!lyp_parse_value(&sleaf->type, &leaf.value_str, NULL, &leaf, NULL, NULL, 0, 0)) {
              ret = -1;
              goto finish;
          }
      }
  
      if (!strcmp(leaf.value_str, can_val)) {
          ret = 1;
      } else {
          ret = 0;
      }
  
  finish:
      lydict_remove(node->module->ctx, leaf.value_str);
      return ret;
  }
  
  /**
   * @brief Resolve instance-identifier predicate in JSON data format.
   *        Does not log.
   *
   * @param[in] prev_mod Previous module to use in case there is no prefix.
   * @param[in] pred Predicate to use.
   * @param[in,out] node Node matching the restriction without
   *                     the predicate. If it does not satisfy the predicate,
   *                     it is set to NULL.
   *
   * @return Number of characters successfully parsed,
   *         positive on success, negative on failure.
   */
  static int
  resolve_instid_predicate(const struct lys_module *prev_mod, const char *pred, struct lyd_node **node, int cur_idx)
  {
      /* ... /node[key=value] ... */
      struct lyd_node_leaf_list *key;
      struct lys_node_leaf **list_keys = NULL;
      struct lys_node_list *slist = NULL;
      const char *model, *name, *value;
      int mod_len, nam_len, val_len, i, has_predicate, parsed;
      struct ly_ctx *ctx = prev_mod->ctx;
  
      assert(pred && node && *node);
  
      parsed = 0;
      do {
          if ((i = parse_predicate(pred + parsed, &model, &mod_len, &name, &nam_len, &value, &val_len, &has_predicate)) < 1) {
              return -parsed + i;
          }
          parsed += i;
  
          if (!(*node)) {
              /* just parse it all */
              continue;
          }
  
          /* target */
          if (name[0] == '.') {
              /* leaf-list value */
              if ((*node)->schema->nodetype != LYS_LEAFLIST) {
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Instance identifier expects leaf-list, but have %s \"%s\".",
                         strnodetype((*node)->schema->nodetype), (*node)->schema->name);
                  parsed = -1;
                  goto cleanup;
              }
  
              /* check the value */
              if (!valequal((*node)->schema, value, val_len, ((struct lyd_node_leaf_list *)*node)->value_str)) {
                  *node = NULL;
                  goto cleanup;
              }
  
          } else if (isdigit(name[0])) {
              assert(!value);
  
              /* keyless list position */
              if ((*node)->schema->nodetype != LYS_LIST) {
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Instance identifier expects list, but have %s \"%s\".",
                         strnodetype((*node)->schema->nodetype), (*node)->schema->name);
                  parsed = -1;
                  goto cleanup;
              }
  
              if (((struct lys_node_list *)(*node)->schema)->keys) {
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Instance identifier expects list without keys, but have list \"%s\".",
                         (*node)->schema->name);
                  parsed = -1;
                  goto cleanup;
              }
  
              /* check the index */
              if (atoi(name) != cur_idx) {
                  *node = NULL;
                  goto cleanup;
              }
  
          } else {
              /* list key value */
              if ((*node)->schema->nodetype != LYS_LIST) {
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Instance identifier expects list, but have %s \"%s\".",
                         strnodetype((*node)->schema->nodetype), (*node)->schema->name);
                  parsed = -1;
                  goto cleanup;
              }
              slist = (struct lys_node_list *)(*node)->schema;
  
              /* prepare key array */
              if (!list_keys) {
                  list_keys = malloc(slist->keys_size * sizeof *list_keys);
                  LY_CHECK_ERR_RETURN(!list_keys, LOGMEM(ctx), -1);
                  for (i = 0; i < slist->keys_size; ++i) {
                      list_keys[i] = slist->keys[i];
                  }
              }
  
              /* find the schema key leaf */
              for (i = 0; i < slist->keys_size; ++i) {
                  if (list_keys[i] && !strncmp(list_keys[i]->name, name, nam_len) && !list_keys[i]->name[nam_len]) {
                      break;
                  }
              }
              if (i == slist->keys_size) {
                  /* this list has no such key */
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Instance identifier expects list with the key \"%.*s\","
                         " but list \"%s\" does not define it.", nam_len, name, slist->name);
                  parsed = -1;
                  goto cleanup;
              }
  
              /* check module */
              if (model) {
                  if (strncmp(list_keys[i]->module->name, model, mod_len) || list_keys[i]->module->name[mod_len]) {
                      LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Instance identifier expects key \"%s\" from module \"%.*s\", not \"%s\".",
                             list_keys[i]->name, model, mod_len, list_keys[i]->module->name);
                      parsed = -1;
                      goto cleanup;
                  }
              } else {
                  if (list_keys[i]->module != prev_mod) {
                      LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Instance identifier expects key \"%s\" from module \"%s\", not \"%s\".",
                             list_keys[i]->name, prev_mod->name, list_keys[i]->module->name);
                      parsed = -1;
                      goto cleanup;
                  }
              }
  
              /* find the actual data key */
              for (key = (struct lyd_node_leaf_list *)(*node)->child; key; key = (struct lyd_node_leaf_list *)key->next) {
                  if (key->schema == (struct lys_node *)list_keys[i]) {
                      break;
                  }
              }
              if (!key) {
                  /* list instance is missing a key? definitely should not happen */
                  LOGINT(ctx);
                  parsed = -1;
                  goto cleanup;
              }
  
              /* check the value */
              if (!valequal(key->schema, value, val_len, key->value_str)) {
                  *node = NULL;
                  /* we still want to parse the whole predicate */
                  continue;
              }
  
              /* everything is fine, mark this key as resolved */
              list_keys[i] = NULL;
          }
      } while (has_predicate);
  
      /* check that all list keys were specified */
      if (*node && list_keys) {
          for (i = 0; i < slist->keys_size; ++i) {
              if (list_keys[i]) {
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Instance identifier is missing list key \"%s\".", list_keys[i]->name);
                  parsed = -1;
                  goto cleanup;
              }
          }
      }
  
  cleanup:
      free(list_keys);
      return parsed;
  }
  
  static int
  check_xpath(struct lys_node *node, int check_place)
  {
      struct lys_node *parent;
      struct lyxp_set set;
      enum int_log_opts prev_ilo;
  
      if (check_place) {
          parent = node;
          while (parent) {
              if (parent->nodetype == LYS_GROUPING) {
                  /* unresolved grouping, skip for now (will be checked later) */
                  return EXIT_SUCCESS;
              }
              if (parent->nodetype == LYS_AUGMENT) {
                  if (!((struct lys_node_augment *)parent)->target) {
                      /* unresolved augment, skip for now (will be checked later) */
                      return EXIT_FAILURE;
                  } else {
                      parent = ((struct lys_node_augment *)parent)->target;
                      continue;
                  }
              }
              parent = parent->parent;
          }
      }
  
      memset(&set, 0, sizeof set);
  
      /* produce just warnings */
      ly_ilo_change(NULL, ILO_ERR2WRN, &prev_ilo, NULL);
      lyxp_node_atomize(node, &set, 1);
      ly_ilo_restore(NULL, prev_ilo, NULL, 0);
  
      if (set.val.snodes) {
          free(set.val.snodes);
      }
      return EXIT_SUCCESS;
  }
  
  static int
  check_leafref_config(struct lys_node_leaf *leaf, struct lys_type *type)
  {
      unsigned int i;
  
      if (type->base == LY_TYPE_LEAFREF) {
          if ((leaf->flags & LYS_CONFIG_W) && type->info.lref.target && type->info.lref.req != -1 &&
                  (type->info.lref.target->flags & LYS_CONFIG_R)) {
              LOGVAL(leaf->module->ctx, LYE_SPEC, LY_VLOG_LYS, leaf, "The leafref %s is config but refers to a non-config %s.",
                     strnodetype(leaf->nodetype), strnodetype(type->info.lref.target->nodetype));
              return -1;
          }
          /* we can skip the test in case the leafref is not yet resolved. In that case the test is done in the time
           * of leafref resolving (lys_leaf_add_leafref_target()) */
      } else if (type->base == LY_TYPE_UNION) {
          for (i = 0; i < type->info.uni.count; i++) {
              if (check_leafref_config(leaf, &type->info.uni.types[i])) {
                  return -1;
              }
          }
      }
      return 0;
  }
  
  /**
   * @brief Passes config flag down to children, skips nodes without config flags.
   * Logs.
   *
   * @param[in] node Siblings and their children to have flags changed.
   * @param[in] clear Flag to clear all config flags if parent is LYS_NOTIF, LYS_INPUT, LYS_OUTPUT, LYS_RPC.
   * @param[in] flags Flags to assign to all the nodes.
   * @param[in,out] unres List of unresolved items.
   *
   * @return 0 on success, -1 on error.
   */
  int
  inherit_config_flag(struct lys_node *node, int flags, int clear)
  {
      struct lys_node_leaf *leaf;
      struct ly_ctx *ctx;
  
      if (!node) {
          return 0;
      }
  
      assert(!(flags ^ (flags & LYS_CONFIG_MASK)));
      ctx = node->module->ctx;
  
      LY_TREE_FOR(node, node) {
          if (clear) {
              node->flags &= ~LYS_CONFIG_MASK;
              node->flags &= ~LYS_CONFIG_SET;
          } else {
              if (node->flags & LYS_CONFIG_SET) {
                  /* skip nodes with an explicit config value */
                  if ((flags & LYS_CONFIG_R) && (node->flags & LYS_CONFIG_W)) {
                      LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, node, "true", "config");
                      LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "State nodes cannot have configuration nodes as children.");
                      return -1;
                  }
                  continue;
              }
  
              if (!(node->nodetype & (LYS_USES | LYS_GROUPING))) {
                  node->flags = (node->flags & ~LYS_CONFIG_MASK) | flags;
                  /* check that configuration lists have keys */
                  if ((node->nodetype == LYS_LIST) && (node->flags & LYS_CONFIG_W)
                          && !((struct lys_node_list *)node)->keys_size) {
                      LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_LYS, node, "key", "list");
                      return -1;
                  }
              }
          }
          if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) {
              if (inherit_config_flag(node->child, flags, clear)) {
                  return -1;
              }
          } else if (node->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
              leaf = (struct lys_node_leaf *)node;
              if (check_leafref_config(leaf, &leaf->type)) {
                  return -1;
              }
          }
      }
  
      return 0;
  }
  
  /**
   * @brief Resolve augment target. Logs directly.
   *
   * @param[in] aug Augment to use.
   * @param[in] uses Parent where to start the search in. If set, uses augment, if not, standalone augment.
   * @param[in,out] unres List of unresolved items.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  static int
  resolve_augment(struct lys_node_augment *aug, struct lys_node *uses, struct unres_schema *unres)
  {
      int rc;
      struct lys_node *sub, *next;
      struct lys_module *mod;
      struct ly_set *set;
      struct ly_ctx *ctx;
      struct lys_node_case *c;
  
      assert(aug);
      mod = lys_main_module(aug->module);
      ctx = mod->ctx;
  
      /* set it as not applied for now */
      aug->flags |= LYS_NOTAPPLIED;
  
      /* it can already be resolved in case we returned EXIT_FAILURE from if block below */
      if (!aug->target) {
          /* resolve target node */
          rc = resolve_schema_nodeid(aug->target_name, uses, (uses ? NULL : lys_node_module((struct lys_node *)aug)), &set, 0, 0);
          if (rc == -1) {
              LOGVAL(ctx, LYE_PATH, LY_VLOG_LYS, aug);
              return -1;
          }
          if (!set) {
              LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, aug, "augment", aug->target_name);
              return EXIT_FAILURE;
          }
          aug->target = set->set.s[0];
          ly_set_free(set);
      }
  
      /* make this module implemented if the target module is (if the target is in an unimplemented module,
       * it is fine because when we will be making that module implemented, its augment will be applied
       * and that augment target module made implemented, recursively) */
      if (mod->implemented && !lys_node_module(aug->target)->implemented) {
          lys_node_module(aug->target)->implemented = 1;
          if (unres_schema_add_node(lys_node_module(aug->target), unres, NULL, UNRES_MOD_IMPLEMENT, NULL) == -1) {
              return -1;
          }
      }
  
      /* check for mandatory nodes - if the target node is in another module
       * the added nodes cannot be mandatory
       */
      if (!aug->parent && (lys_node_module((struct lys_node *)aug) != lys_node_module(aug->target))
              && (rc = lyp_check_mandatory_augment(aug, aug->target))) {
          return rc;
      }
  
      /* check augment target type and then augment nodes type */
      if (aug->target->nodetype & (LYS_CONTAINER | LYS_LIST)) {
          LY_TREE_FOR(aug->child, sub) {
              if (!(sub->nodetype & (LYS_ANYDATA | LYS_CONTAINER | LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_USES
                                     | LYS_CHOICE | LYS_ACTION | LYS_NOTIF))) {
                  LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_LYS, aug, strnodetype(sub->nodetype), "augment");
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Cannot augment \"%s\" with a \"%s\".",
                         strnodetype(aug->target->nodetype), strnodetype(sub->nodetype));
                  return -1;
              }
          }
      } else if (aug->target->nodetype & (LYS_CASE | LYS_INPUT | LYS_OUTPUT | LYS_NOTIF)) {
          LY_TREE_FOR(aug->child, sub) {
              if (!(sub->nodetype & (LYS_ANYDATA | LYS_CONTAINER | LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_USES | LYS_CHOICE))) {
                  LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_LYS, aug, strnodetype(sub->nodetype), "augment");
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Cannot augment \"%s\" with a \"%s\".",
                         strnodetype(aug->target->nodetype), strnodetype(sub->nodetype));
                  return -1;
              }
          }
      } else if (aug->target->nodetype == LYS_CHOICE) {
          LY_TREE_FOR(aug->child, sub) {
              if (!(sub->nodetype & (LYS_CASE | LYS_ANYDATA | LYS_CONTAINER | LYS_LEAF | LYS_LIST | LYS_LEAFLIST))) {
                  LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_LYS, aug, strnodetype(sub->nodetype), "augment");
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Cannot augment \"%s\" with a \"%s\".",
                         strnodetype(aug->target->nodetype), strnodetype(sub->nodetype));
                  return -1;
              }
          }
      } else {
          LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, aug, aug->target_name, "target-node");
          LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Invalid augment target node type \"%s\".", strnodetype(aug->target->nodetype));
          return -1;
      }
  
      /* check identifier uniqueness as in lys_node_addchild() */
      LY_TREE_FOR(aug->child, sub) {
          if (lys_check_id(sub, aug->target, NULL)) {
              return -1;
          }
      }
  
      if (aug->target->nodetype == LYS_CHOICE) {
          /* in the case of implicit cases, we have to create the nodes representing them
           * to serve as a target of the augments, they won't be printed, but they are expected in the tree */
          LY_TREE_FOR_SAFE(aug->child, next, sub) {
              if (sub->nodetype == LYS_CASE) {
                  /* explicit case */
                  continue;
              }
  
              c = calloc(1, sizeof *c);
              LY_CHECK_ERR_RETURN(!c, LOGMEM(ctx), EXIT_FAILURE);
              c->name = lydict_insert(ctx, sub->name, 0);
              c->flags = LYS_IMPLICIT | (sub->flags & LYS_CONFIG_MASK);
              c->module = sub->module;
              c->nodetype = LYS_CASE;
              c->parent = sub->parent;
              c->prev = sub->prev != sub ? sub->prev : (struct lys_node*)c;
              if (c->prev->next) {
                  c->prev->next = (struct lys_node*)c;
              } else {
                  c->parent->child = (struct lys_node*)c;
              }
              c->next = sub->next;
              if (c->next) {
                  c->next->prev = (struct lys_node*)c;
              } else {
                  c->parent->child->prev = (struct lys_node*)c;
              }
              c->child = sub;
              sub->prev = sub;
              sub->next = NULL;
              sub->parent = (struct lys_node*)c;
          }
      }
  
      LY_TREE_DFS_BEGIN(aug->child, next, sub) {
          if (sub->nodetype == LYS_ACTION) {
              /* we need to check parents */
              for (sub = aug->target; sub; sub = lys_parent(sub)) {
                  if ((sub->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF))
                          || ((sub->nodetype == LYS_LIST) && !((struct lys_node_list *)sub)->keys)) {
                      LOGVAL(ctx, LYE_INPAR, LY_VLOG_LYS, aug->target, strnodetype(sub->nodetype), "action");
                      LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, aug, "augment", aug->target_name);
                      return -1;
                  }
              }
              break;
          }
          LY_TREE_DFS_END(aug->child, next, sub);
      }
  
      if (!aug->child) {
          /* empty augment, nothing to connect, but it is techincally applied */
          LOGWRN(ctx, "Augment \"%s\" without children.", aug->target_name);
          aug->flags &= ~LYS_NOTAPPLIED;
      } else if ((aug->parent || mod->implemented) && apply_aug(aug, unres)) {
          /* we try to connect the augment only in case the module is implemented or
           * the augment applies on the used grouping, anyway we failed here */
          return -1;
      }
  
      return EXIT_SUCCESS;
  }
  
  static int
  resolve_extension(struct unres_ext *info, struct lys_ext_instance **ext, struct unres_schema *unres)
  {
      enum LY_VLOG_ELEM vlog_type;
      void *vlog_node;
      unsigned int i, j;
      struct lys_ext *e;
      char *ext_name, *ext_prefix, *tmp;
      struct lyxml_elem *next_yin, *yin;
      const struct lys_module *mod;
      struct lys_ext_instance *tmp_ext;
      struct ly_ctx *ctx = NULL;
      LYEXT_TYPE etype;
  
      switch (info->parent_type) {
      case LYEXT_PAR_NODE:
          vlog_node = info->parent;
          vlog_type = LY_VLOG_LYS;
          break;
      case LYEXT_PAR_MODULE:
      case LYEXT_PAR_IMPORT:
      case LYEXT_PAR_INCLUDE:
          vlog_node = NULL;
          vlog_type = LY_VLOG_LYS;
          break;
      default:
          vlog_node = NULL;
          vlog_type = LY_VLOG_NONE;
          break;
      }
  
      if (info->datatype == LYS_IN_YIN) {
          /* YIN */
  
          /* get the module where the extension is supposed to be defined */
          mod = lyp_get_import_module_ns(info->mod, info->data.yin->ns->value);
          if (!mod) {
              LOGVAL(ctx, LYE_INSTMT, vlog_type, vlog_node, info->data.yin->name);
              return EXIT_FAILURE;
          }
          ctx = mod->ctx;
  
          /* find the extension definition */
          e = NULL;
          for (i = 0; i < mod->extensions_size; i++) {
              if (ly_strequal(mod->extensions[i].name, info->data.yin->name, 1)) {
                  e = &mod->extensions[i];
                  break;
              }
          }
          /* try submodules */
          for (j = 0; !e && j < mod->inc_size; j++) {
              for (i = 0; i < mod->inc[j].submodule->extensions_size; i++) {
                  if (ly_strequal(mod->inc[j].submodule->extensions[i].name, info->data.yin->name, 1)) {
                      e = &mod->inc[j].submodule->extensions[i];
                      break;
                  }
              }
          }
          if (!e) {
              LOGVAL(ctx, LYE_INSTMT, vlog_type, vlog_node, info->data.yin->name);
              return EXIT_FAILURE;
          }
  
          /* we have the extension definition, so now it cannot be forward referenced and error is always fatal */
  
          if (e->plugin && e->plugin->check_position) {
              /* common part - we have plugin with position checking function, use it first */
              if ((*e->plugin->check_position)(info->parent, info->parent_type, info->substmt)) {
                  /* extension is not allowed here */
                  LOGVAL(ctx, LYE_INSTMT, vlog_type, vlog_node, e->name);
                  return -1;
              }
          }
  
          /* extension type-specific part - allocation */
          if (e->plugin) {
              etype = e->plugin->type;
          } else {
              /* default type */
              etype = LYEXT_FLAG;
          }
          switch (etype) {
          case LYEXT_FLAG:
              (*ext) = calloc(1, sizeof(struct lys_ext_instance));
              break;
          case LYEXT_COMPLEX:
              (*ext) = calloc(1, ((struct lyext_plugin_complex*)e->plugin)->instance_size);
              break;
          case LYEXT_ERR:
              /* we never should be here */
              LOGINT(ctx);
              return -1;
          }
          LY_CHECK_ERR_RETURN(!*ext, LOGMEM(ctx), -1);
  
          /* common part for all extension types */
          (*ext)->def = e;
          (*ext)->parent = info->parent;
          (*ext)->parent_type = info->parent_type;
          (*ext)->insubstmt = info->substmt;
          (*ext)->insubstmt_index = info->substmt_index;
          (*ext)->ext_type = e->plugin ? e->plugin->type : LYEXT_FLAG;
          (*ext)->flags |= e->plugin ? e->plugin->flags : 0;
  
          if (e->argument) {
              if (!(e->flags & LYS_YINELEM)) {
                  (*ext)->arg_value = lyxml_get_attr(info->data.yin, e->argument, NULL);
                  if (!(*ext)->arg_value) {
                      LOGVAL(ctx, LYE_MISSARG, LY_VLOG_NONE, NULL, e->argument, info->data.yin->name);
                      return -1;
                  }
  
                  (*ext)->arg_value = lydict_insert(mod->ctx, (*ext)->arg_value, 0);
              } else {
                  LY_TREE_FOR_SAFE(info->data.yin->child, next_yin, yin) {
                      if (ly_strequal(yin->name, e->argument, 1)) {
                          (*ext)->arg_value = lydict_insert(mod->ctx, yin->content, 0);
                          lyxml_free(mod->ctx, yin);
                          break;
                      }
                  }
              }
          }
  
          if ((*ext)->flags & LYEXT_OPT_VALID &&
              (info->parent_type == LYEXT_PAR_NODE || info->parent_type == LYEXT_PAR_TPDF)) {
              ((struct lys_node *)info->parent)->flags |= LYS_VALID_EXT;
          }
  
          (*ext)->nodetype = LYS_EXT;
          (*ext)->module = info->mod;
  
          /* extension type-specific part - parsing content */
          switch (etype) {
          case LYEXT_FLAG:
              LY_TREE_FOR_SAFE(info->data.yin->child, next_yin, yin) {
                  if (!yin->ns) {
                      /* garbage */
                      lyxml_free(mod->ctx, yin);
                      continue;
                  } else if (!strcmp(yin->ns->value, LY_NSYIN)) {
                      /* standard YANG statements are not expected here */
                      LOGVAL(ctx, LYE_INCHILDSTMT, vlog_type, vlog_node, yin->name, info->data.yin->name);
                      return -1;
                  } else if (yin->ns == info->data.yin->ns &&
                          (e->flags & LYS_YINELEM) && ly_strequal(yin->name, e->argument, 1)) {
                      /* we have the extension's argument */
                      if ((*ext)->arg_value) {
                          LOGVAL(ctx, LYE_TOOMANY, vlog_type, vlog_node, yin->name, info->data.yin->name);
                          return -1;
                      }
                      (*ext)->arg_value = yin->content;
                      yin->content = NULL;
                      lyxml_free(mod->ctx, yin);
                  } else {
                      /* extension instance */
                      if (lyp_yin_parse_subnode_ext(info->mod, *ext, LYEXT_PAR_EXTINST, yin,
                                                    LYEXT_SUBSTMT_SELF, 0, unres)) {
                          return -1;
                      }
  
                      continue;
                  }
              }
              break;
          case LYEXT_COMPLEX:
              ((struct lys_ext_instance_complex*)(*ext))->substmt = ((struct lyext_plugin_complex*)e->plugin)->substmt;
              if (lyp_yin_parse_complex_ext(info->mod, (struct lys_ext_instance_complex*)(*ext), info->data.yin, unres)) {
                  /* TODO memory cleanup */
                  return -1;
              }
              break;
          default:
              break;
          }
  
          /* TODO - lyext_check_result_clb, other than LYEXT_FLAG plugins */
  
      } else {
          /* YANG */
  
          ext_prefix = (char *)(*ext)->def;
          tmp = strchr(ext_prefix, ':');
          if (!tmp) {
              LOGVAL(ctx, LYE_INSTMT, vlog_type, vlog_node, ext_prefix);
              goto error;
          }
          ext_name = tmp + 1;
  
          /* get the module where the extension is supposed to be defined */
          mod = lyp_get_module(info->mod, ext_prefix, tmp - ext_prefix, NULL, 0, 0);
          if (!mod) {
              LOGVAL(ctx, LYE_INSTMT, vlog_type, vlog_node, ext_prefix);
              return EXIT_FAILURE;
          }
          ctx = mod->ctx;
  
          /* find the extension definition */
          e = NULL;
          for (i = 0; i < mod->extensions_size; i++) {
              if (ly_strequal(mod->extensions[i].name, ext_name, 0)) {
                  e = &mod->extensions[i];
                  break;
              }
          }
          /* try submodules */
          for (j = 0; !e && j < mod->inc_size; j++) {
              for (i = 0; i < mod->inc[j].submodule->extensions_size; i++) {
                  if (ly_strequal(mod->inc[j].submodule->extensions[i].name, ext_name, 0)) {
                      e = &mod->inc[j].submodule->extensions[i];
                      break;
                  }
              }
          }
          if (!e) {
              LOGVAL(ctx, LYE_INSTMT, vlog_type, vlog_node, ext_prefix);
              return EXIT_FAILURE;
          }
  
          (*ext)->flags &= ~LYEXT_OPT_YANG;
          (*ext)->def = NULL;
  
          /* we have the extension definition, so now it cannot be forward referenced and error is always fatal */
  
          if (e->plugin && e->plugin->check_position) {
              /* common part - we have plugin with position checking function, use it first */
              if ((*e->plugin->check_position)(info->parent, info->parent_type, info->substmt)) {
                  /* extension is not allowed here */
                  LOGVAL(ctx, LYE_INSTMT, vlog_type, vlog_node, e->name);
                  goto error;
              }
          }
  
          if (e->argument && !(*ext)->arg_value) {
              LOGVAL(ctx, LYE_MISSARG, LY_VLOG_NONE, NULL, e->argument, ext_name);
              goto error;
          }
  
          /* extension common part */
          (*ext)->def = e;
          (*ext)->parent = info->parent;
          (*ext)->ext_type = e->plugin ? e->plugin->type : LYEXT_FLAG;
          (*ext)->flags |= e->plugin ? e->plugin->flags : 0;
  
          if ((*ext)->flags & LYEXT_OPT_VALID &&
              (info->parent_type == LYEXT_PAR_NODE || info->parent_type == LYEXT_PAR_TPDF)) {
              ((struct lys_node *)info->parent)->flags |= LYS_VALID_EXT;
          }
  
          (*ext)->module = info->mod;
          (*ext)->nodetype = LYS_EXT;
  
          /* extension type-specific part */
          if (e->plugin) {
              etype = e->plugin->type;
          } else {
              /* default type */
              etype = LYEXT_FLAG;
          }
          switch (etype) {
          case LYEXT_FLAG:
              /* nothing change */
              break;
          case LYEXT_COMPLEX:
              tmp_ext = realloc(*ext, ((struct lyext_plugin_complex*)e->plugin)->instance_size);
              LY_CHECK_ERR_GOTO(!tmp_ext, LOGMEM(ctx), error);
              memset((char *)tmp_ext + offsetof(struct lys_ext_instance_complex, content), 0,
                     ((struct lyext_plugin_complex*)e->plugin)->instance_size - offsetof(struct lys_ext_instance_complex, content));
              (*ext) = tmp_ext;
              ((struct lys_ext_instance_complex*)(*ext))->substmt = ((struct lyext_plugin_complex*)e->plugin)->substmt;
              if (info->data.yang) {
                  *tmp = ':';
                  if (yang_parse_ext_substatement(info->mod, unres, info->data.yang->ext_substmt, ext_prefix,
                                                  (struct lys_ext_instance_complex*)(*ext))) {
                      goto error;
                  }
                  if (yang_fill_extcomplex_module(info->mod->ctx, (struct lys_ext_instance_complex*)(*ext), ext_prefix,
                                                  info->data.yang->ext_modules, info->mod->implemented)) {
                      goto error;
                  }
              }
              if (lyp_mand_check_ext((struct lys_ext_instance_complex*)(*ext), ext_prefix)) {
                  goto error;
              }
              break;
          case LYEXT_ERR:
              /* we never should be here */
              LOGINT(ctx);
              goto error;
          }
  
          if (yang_check_ext_instance(info->mod, &(*ext)->ext, (*ext)->ext_size, *ext, unres)) {
              goto error;
          }
          free(ext_prefix);
      }
  
      return EXIT_SUCCESS;
  error:
      free(ext_prefix);
      return -1;
  }
  
  /**
   * @brief Resolve (find) choice default case. Does not log.
   *
   * @param[in] choic Choice to use.
   * @param[in] dflt Name of the default case.
   *
   * @return Pointer to the default node or NULL.
   */
  static struct lys_node *
  resolve_choice_dflt(struct lys_node_choice *choic, const char *dflt)
  {
      struct lys_node *child, *ret;
  
      LY_TREE_FOR(choic->child, child) {
          if (child->nodetype == LYS_USES) {
              ret = resolve_choice_dflt((struct lys_node_choice *)child, dflt);
              if (ret) {
                  return ret;
              }
          }
  
          if (ly_strequal(child->name, dflt, 1) && (child->nodetype & (LYS_ANYDATA | LYS_CASE
                  | LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CHOICE))) {
              return child;
          }
      }
  
      return NULL;
  }
  
  /**
   * @brief Resolve uses, apply augments, refines. Logs directly.
   *
   * @param[in] uses Uses to use.
   * @param[in,out] unres List of unresolved items.
   *
   * @return EXIT_SUCCESS on success, -1 on error.
   */
  static int
  resolve_uses(struct lys_node_uses *uses, struct unres_schema *unres)
  {
      struct ly_ctx *ctx = uses->module->ctx; /* shortcut */
      struct lys_node *node = NULL, *next, *iter, **refine_nodes = NULL;
      struct lys_node *node_aux, *parent, *tmp;
      struct lys_node_leaflist *llist;
      struct lys_node_leaf *leaf;
      struct lys_refine *rfn;
      struct lys_restr *must, **old_must;
      struct lys_iffeature *iff, **old_iff;
      int i, j, k, rc;
      uint8_t size, *old_size;
      unsigned int usize, usize1, usize2;
  
      assert(uses->grp);
  
      /* check that the grouping is resolved (no unresolved uses inside) */
      assert(!uses->grp->unres_count);
  
      /* copy the data nodes from grouping into the uses context */
      LY_TREE_FOR(uses->grp->child, node_aux) {
          if (node_aux->nodetype & LYS_GROUPING) {
              /* do not instantiate groupings from groupings */
              continue;
          }
          node = lys_node_dup(uses->module, (struct lys_node *)uses, node_aux, unres, 0);
          if (!node) {
              LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, uses, uses->grp->name, "uses");
              LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Copying data from grouping failed.");
              goto fail;
          }
          /* test the name of siblings */
          LY_TREE_FOR((uses->parent) ? *lys_child(uses->parent, LYS_USES) : lys_main_module(uses->module)->data, tmp) {
              if (!(tmp->nodetype & (LYS_USES | LYS_GROUPING | LYS_CASE)) && ly_strequal(tmp->name, node_aux->name, 1)) {
                  goto fail;
              }
          }
      }
  
      /* we managed to copy the grouping, the rest must be possible to resolve */
  
      if (uses->refine_size) {
          refine_nodes = malloc(uses->refine_size * sizeof *refine_nodes);
          LY_CHECK_ERR_GOTO(!refine_nodes, LOGMEM(ctx), fail);
      }
  
      /* apply refines */
      for (i = 0; i < uses->refine_size; i++) {
          rfn = &uses->refine[i];
          rc = resolve_descendant_schema_nodeid(rfn->target_name, uses->child,
                                                LYS_NO_RPC_NOTIF_NODE | LYS_ACTION | LYS_NOTIF,
                                                0, (const struct lys_node **)&node);
          if (rc || !node) {
              LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, uses, rfn->target_name, "refine");
              goto fail;
          }
  
          if (rfn->target_type && !(node->nodetype & rfn->target_type)) {
              LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, uses, rfn->target_name, "refine");
              LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Refine substatements not applicable to the target-node.");
              goto fail;
          }
          refine_nodes[i] = node;
  
          /* description on any nodetype */
          if (rfn->dsc) {
              lydict_remove(ctx, node->dsc);
              node->dsc = lydict_insert(ctx, rfn->dsc, 0);
          }
  
          /* reference on any nodetype */
          if (rfn->ref) {
              lydict_remove(ctx, node->ref);
              node->ref = lydict_insert(ctx, rfn->ref, 0);
          }
  
          /* config on any nodetype,
           * in case of notification or rpc/action, the config is not applicable (there is no config status) */
          if ((rfn->flags & LYS_CONFIG_MASK) && (node->flags & LYS_CONFIG_MASK)) {
              node->flags &= ~LYS_CONFIG_MASK;
              node->flags |= (rfn->flags & LYS_CONFIG_MASK);
          }
  
          /* default value ... */
          if (rfn->dflt_size) {
              if (node->nodetype == LYS_LEAF) {
                  /* leaf */
                  leaf = (struct lys_node_leaf *)node;
  
                  /* replace default value */
                  lydict_remove(ctx, leaf->dflt);
                  leaf->dflt = lydict_insert(ctx, rfn->dflt[0], 0);
  
                  /* check the default value */
                  if (unres_schema_add_node(leaf->module, unres, &leaf->type, UNRES_TYPE_DFLT,
                                            (struct lys_node *)(&leaf->dflt)) == -1) {
                      goto fail;
                  }
              } else if (node->nodetype == LYS_LEAFLIST) {
                  /* leaf-list */
                  llist = (struct lys_node_leaflist *)node;
  
                  /* remove complete set of defaults in target */
                  for (j = 0; j < llist->dflt_size; j++) {
                      lydict_remove(ctx, llist->dflt[j]);
                  }
                  free(llist->dflt);
  
                  /* copy the default set from refine */
                  llist->dflt = malloc(rfn->dflt_size * sizeof *llist->dflt);
                  LY_CHECK_ERR_GOTO(!llist->dflt, LOGMEM(ctx), fail);
                  llist->dflt_size = rfn->dflt_size;
                  for (j = 0; j < llist->dflt_size; j++) {
                      llist->dflt[j] = lydict_insert(ctx, rfn->dflt[j], 0);
                  }
  
                  /* check default value */
                  for (j = 0; j < llist->dflt_size; j++) {
                      if (unres_schema_add_node(llist->module, unres, &llist->type, UNRES_TYPE_DFLT,
                                                (struct lys_node *)(&llist->dflt[j])) == -1) {
                          goto fail;
                      }
                  }
              }
          }
  
          /* mandatory on leaf, anyxml or choice */
          if (rfn->flags & LYS_MAND_MASK) {
              /* remove current value */
              node->flags &= ~LYS_MAND_MASK;
  
              /* set new value */
              node->flags |= (rfn->flags & LYS_MAND_MASK);
  
              if (rfn->flags & LYS_MAND_TRUE) {
                  /* check if node has default value */
                  if ((node->nodetype & LYS_LEAF) && ((struct lys_node_leaf *)node)->dflt) {
                      LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, uses,
                             "The \"mandatory\" statement is forbidden on leaf with \"default\".");
                      goto fail;
                  }
                  if ((node->nodetype & LYS_CHOICE) && ((struct lys_node_choice *)node)->dflt) {
                      LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, uses,
                             "The \"mandatory\" statement is forbidden on choices with \"default\".");
                      goto fail;
                  }
              }
          }
  
          /* presence on container */
          if ((node->nodetype & LYS_CONTAINER) && rfn->mod.presence) {
              lydict_remove(ctx, ((struct lys_node_container *)node)->presence);
              ((struct lys_node_container *)node)->presence = lydict_insert(ctx, rfn->mod.presence, 0);
          }
  
          /* min/max-elements on list or leaf-list */
          if (node->nodetype == LYS_LIST) {
              if (rfn->flags & LYS_RFN_MINSET) {
                  ((struct lys_node_list *)node)->min = rfn->mod.list.min;
              }
              if (rfn->flags & LYS_RFN_MAXSET) {
                  ((struct lys_node_list *)node)->max = rfn->mod.list.max;
              }
          } else if (node->nodetype == LYS_LEAFLIST) {
              if (rfn->flags & LYS_RFN_MINSET) {
                  ((struct lys_node_leaflist *)node)->min = rfn->mod.list.min;
              }
              if (rfn->flags & LYS_RFN_MAXSET) {
                  ((struct lys_node_leaflist *)node)->max = rfn->mod.list.max;
              }
          }
  
          /* must in leaf, leaf-list, list, container or anyxml */
          if (rfn->must_size) {
              switch (node->nodetype) {
              case LYS_LEAF:
                  old_size = &((struct lys_node_leaf *)node)->must_size;
                  old_must = &((struct lys_node_leaf *)node)->must;
                  break;
              case LYS_LEAFLIST:
                  old_size = &((struct lys_node_leaflist *)node)->must_size;
                  old_must = &((struct lys_node_leaflist *)node)->must;
                  break;
              case LYS_LIST:
                  old_size = &((struct lys_node_list *)node)->must_size;
                  old_must = &((struct lys_node_list *)node)->must;
                  break;
              case LYS_CONTAINER:
                  old_size = &((struct lys_node_container *)node)->must_size;
                  old_must = &((struct lys_node_container *)node)->must;
                  break;
              case LYS_ANYXML:
              case LYS_ANYDATA:
                  old_size = &((struct lys_node_anydata *)node)->must_size;
                  old_must = &((struct lys_node_anydata *)node)->must;
                  break;
              default:
                  LOGINT(ctx);
                  goto fail;
              }
  
              size = *old_size + rfn->must_size;
              must = realloc(*old_must, size * sizeof *rfn->must);
              LY_CHECK_ERR_GOTO(!must, LOGMEM(ctx), fail);
              for (k = 0, j = *old_size; k < rfn->must_size; k++, j++) {
                  must[j].ext_size = rfn->must[k].ext_size;
                  lys_ext_dup(ctx, rfn->module, rfn->must[k].ext, rfn->must[k].ext_size, &rfn->must[k], LYEXT_PAR_RESTR,
                              &must[j].ext, 0, unres);
                  must[j].expr = lydict_insert(ctx, rfn->must[k].expr, 0);
                  must[j].dsc = lydict_insert(ctx, rfn->must[k].dsc, 0);
                  must[j].ref = lydict_insert(ctx, rfn->must[k].ref, 0);
                  must[j].eapptag = lydict_insert(ctx, rfn->must[k].eapptag, 0);
                  must[j].emsg = lydict_insert(ctx, rfn->must[k].emsg, 0);
                  must[j].flags = rfn->must[k].flags;
              }
  
              *old_must = must;
              *old_size = size;
  
              /* check XPath dependencies again */
              if (unres_schema_add_node(node->module, unres, node, UNRES_XPATH, NULL) == -1) {
                  goto fail;
              }
          }
  
          /* if-feature in leaf, leaf-list, list, container or anyxml */
          if (rfn->iffeature_size) {
              old_size = &node->iffeature_size;
              old_iff = &node->iffeature;
  
              size = *old_size + rfn->iffeature_size;
              iff = realloc(*old_iff, size * sizeof *rfn->iffeature);
              LY_CHECK_ERR_GOTO(!iff, LOGMEM(ctx), fail);
              *old_iff = iff;
  
              for (k = 0, j = *old_size; k < rfn->iffeature_size; k++, j++) {
                  resolve_iffeature_getsizes(&rfn->iffeature[k], &usize1, &usize2);
                  if (usize1) {
                      /* there is something to duplicate */
                      /* duplicate compiled expression */
                      usize = (usize1 / 4) + ((usize1 % 4) ? 1 : 0);
                      iff[j].expr = malloc(usize * sizeof *iff[j].expr);
                      LY_CHECK_ERR_GOTO(!iff[j].expr, LOGMEM(ctx), fail);
                      memcpy(iff[j].expr, rfn->iffeature[k].expr, usize * sizeof *iff[j].expr);
  
                      /* duplicate list of feature pointers */
                      iff[j].features = malloc(usize2 * sizeof *iff[k].features);
                      LY_CHECK_ERR_GOTO(!iff[j].expr, LOGMEM(ctx), fail);
                      memcpy(iff[j].features, rfn->iffeature[k].features, usize2 * sizeof *iff[j].features);
  
                      /* duplicate extensions */
                      iff[j].ext_size = rfn->iffeature[k].ext_size;
                      lys_ext_dup(ctx, rfn->module, rfn->iffeature[k].ext, rfn->iffeature[k].ext_size,
                                  &rfn->iffeature[k], LYEXT_PAR_IFFEATURE, &iff[j].ext, 0, unres);
                  }
                  (*old_size)++;
              }
              assert(*old_size == size);
          }
      }
  
      /* apply augments */
      for (i = 0; i < uses->augment_size; i++) {
          rc = resolve_augment(&uses->augment[i], (struct lys_node *)uses, unres);
          if (rc) {
              goto fail;
          }
      }
  
      /* check refines */
      for (i = 0; i < uses->refine_size; i++) {
          node = refine_nodes[i];
          rfn = &uses->refine[i];
  
          /* config on any nodetype */
          if ((rfn->flags & LYS_CONFIG_MASK) && (node->flags & LYS_CONFIG_MASK)) {
              for (parent = lys_parent(node); parent && parent->nodetype == LYS_USES; parent = lys_parent(parent));
              if (parent && parent->nodetype != LYS_GROUPING && (parent->flags & LYS_CONFIG_MASK) &&
                      ((parent->flags & LYS_CONFIG_MASK) != (rfn->flags & LYS_CONFIG_MASK)) &&
                      (rfn->flags & LYS_CONFIG_W)) {
                  /* setting config true under config false is prohibited */
                  LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, uses, "config", "refine");
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL,
                         "changing config from 'false' to 'true' is prohibited while "
                         "the target's parent is still config 'false'.");
                  goto fail;
              }
  
              /* inherit config change to the target children */
              LY_TREE_DFS_BEGIN(node->child, next, iter) {
                  if (rfn->flags & LYS_CONFIG_W) {
                      if (iter->flags & LYS_CONFIG_SET) {
                          /* config is set explicitely, go to next sibling */
                          next = NULL;
                          goto nextsibling;
                      }
                  } else { /* LYS_CONFIG_R */
                      if ((iter->flags & LYS_CONFIG_SET) && (iter->flags & LYS_CONFIG_W)) {
                          /* error - we would have config data under status data */
                          LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, uses, "config", "refine");
                          LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL,
                                 "changing config from 'true' to 'false' is prohibited while the target "
                                 "has still a children with explicit config 'true'.");
                          goto fail;
                      }
                  }
                  /* change config */
                  iter->flags &= ~LYS_CONFIG_MASK;
                  iter->flags |= (rfn->flags & LYS_CONFIG_MASK);
  
                  /* select next iter - modified LY_TREE_DFS_END */
                  if (iter->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
                      next = NULL;
                  } else {
                      next = iter->child;
                  }
  nextsibling:
                  if (!next) {
                      /* try siblings */
                      next = iter->next;
                  }
                  while (!next) {
                      /* parent is already processed, go to its sibling */
                      iter = lys_parent(iter);
  
                      /* no siblings, go back through parents */
                      if (iter == node) {
                          /* we are done, no next element to process */
                          break;
                      }
                      next = iter->next;
                  }
              }
          }
  
          /* default value */
          if (rfn->dflt_size) {
              if (node->nodetype == LYS_CHOICE) {
                  /* choice */
                  ((struct lys_node_choice *)node)->dflt = resolve_choice_dflt((struct lys_node_choice *)node,
                                                                               rfn->dflt[0]);
                  if (!((struct lys_node_choice *)node)->dflt) {
                      LOGVAL(ctx, LYE_INARG, LY_VLOG_LYS, uses, rfn->dflt[0], "default");
                      goto fail;
                  }
                  if (lyp_check_mandatory_choice(node)) {
                      goto fail;
                  }
              }
          }
  
          /* min/max-elements on list or leaf-list */
          if (node->nodetype == LYS_LIST && ((struct lys_node_list *)node)->max) {
              if (((struct lys_node_list *)node)->min > ((struct lys_node_list *)node)->max) {
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, uses, "Invalid value \"%d\" of \"%s\".", rfn->mod.list.min, "min-elements");
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "\"min-elements\" is bigger than \"max-elements\".");
                  goto fail;
              }
          } else if (node->nodetype == LYS_LEAFLIST && ((struct lys_node_leaflist *)node)->max) {
              if (((struct lys_node_leaflist *)node)->min > ((struct lys_node_leaflist *)node)->max) {
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYS, uses, "Invalid value \"%d\" of \"%s\".", rfn->mod.list.min, "min-elements");
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "\"min-elements\" is bigger than \"max-elements\".");
                  goto fail;
              }
          }
  
          /* additional checks */
          /* default value with mandatory/min-elements */
          if (node->nodetype == LYS_LEAFLIST) {
              llist = (struct lys_node_leaflist *)node;
              if (llist->dflt_size && llist->min) {
                  LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_LYS, uses, rfn->dflt_size ? "default" : "min-elements", "refine");
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL,
                         "The \"min-elements\" statement with non-zero value is forbidden on leaf-lists with the \"default\" statement.");
                  goto fail;
              }
          } else if (node->nodetype == LYS_LEAF) {
              leaf = (struct lys_node_leaf *)node;
              if (leaf->dflt && (leaf->flags & LYS_MAND_TRUE)) {
                  LOGVAL(ctx, LYE_INCHILDSTMT, LY_VLOG_LYS, uses, rfn->dflt_size ? "default" : "mandatory", "refine");
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL,
                         "The \"mandatory\" statement is forbidden on leafs with the \"default\" statement.");
                  goto fail;
              }
          }
  
          /* check for mandatory node in default case, first find the closest parent choice to the changed node */
          if ((rfn->flags & LYS_MAND_TRUE) || rfn->mod.list.min) {
              for (parent = node->parent;
                   parent && !(parent->nodetype & (LYS_CHOICE | LYS_GROUPING | LYS_ACTION | LYS_USES));
                   parent = parent->parent) {
                  if (parent->nodetype == LYS_CONTAINER && ((struct lys_node_container *)parent)->presence) {
                      /* stop also on presence containers */
                      break;
                  }
              }
              /* and if it is a choice with the default case, check it for presence of a mandatory node in it */
              if (parent && parent->nodetype == LYS_CHOICE && ((struct lys_node_choice *)parent)->dflt) {
                  if (lyp_check_mandatory_choice(parent)) {
                      goto fail;
                  }
              }
          }
      }
      free(refine_nodes);
  
      /* check list config after all the refines were applied */
      LY_TREE_DFS_BEGIN((struct lys_node *)uses, next, iter) {
          if ((iter->nodetype == LYS_LIST) && (iter->flags & LYS_CONFIG_W)
                  && !((struct lys_node_list *)iter)->keys_size) {
              LOGVAL(ctx, LYE_MISSCHILDSTMT, LY_VLOG_LYS, iter, "key", "list");
              goto fail;
          }
          LY_TREE_DFS_END((struct lys_node *)uses, next, iter);
      }
  
      return EXIT_SUCCESS;
  
  fail:
      LY_TREE_FOR_SAFE(uses->child, next, iter) {
          lys_node_free(iter, NULL, 0);
      }
      free(refine_nodes);
      return -1;
  }
  
  void
  resolve_identity_backlink_update(struct lys_ident *der, struct lys_ident *base)
  {
      int i;
  
      assert(der && base);
  
      if (!base->der) {
          /* create a set for backlinks if it does not exist */
          base->der = ly_set_new();
      }
      /* store backlink */
      ly_set_add(base->der, der, LY_SET_OPT_USEASLIST);
  
      /* do it recursively */
      for (i = 0; i < base->base_size; i++) {
          resolve_identity_backlink_update(der, base->base[i]);
      }
  }
  
  /**
   * @brief Resolve base identity recursively. Does not log.
   *
   * @param[in] module Main module.
   * @param[in] ident Identity to use.
   * @param[in] basename Base name of the identity.
   * @param[out] ret Pointer to the resolved identity. Can be NULL.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on crucial error.
   */
  static int
  resolve_base_ident_sub(const struct lys_module *module, struct lys_ident *ident, const char *basename,
                         struct unres_schema *unres, struct lys_ident **ret)
  {
      uint32_t i, j;
      struct lys_ident *base = NULL;
      struct ly_ctx *ctx = module->ctx;
  
      assert(ret);
  
      /* search module */
      for (i = 0; i < module->ident_size; i++) {
          if (!strcmp(basename, module->ident[i].name)) {
  
              if (!ident) {
                  /* just search for type, so do not modify anything, just return
                   * the base identity pointer */
                  *ret = &module->ident[i];
                  return EXIT_SUCCESS;
              }
  
              base = &module->ident[i];
              goto matchfound;
          }
      }
  
      /* search submodules */
      for (j = 0; j < module->inc_size && module->inc[j].submodule; j++) {
          for (i = 0; i < module->inc[j].submodule->ident_size; i++) {
              if (!strcmp(basename, module->inc[j].submodule->ident[i].name)) {
  
                  if (!ident) {
                      *ret = &module->inc[j].submodule->ident[i];
                      return EXIT_SUCCESS;
                  }
  
                  base = &module->inc[j].submodule->ident[i];
                  goto matchfound;
              }
          }
      }
  
  matchfound:
      /* we found it somewhere */
      if (base) {
          /* is it already completely resolved? */
          for (i = 0; i < unres->count; i++) {
              if ((unres->item[i] == base) && (unres->type[i] == UNRES_IDENT)) {
                  /* identity found, but not yet resolved, so do not return it in *res and try it again later */
  
                  /* simple check for circular reference,
                   * the complete check is done as a side effect of using only completely
                   * resolved identities (previous check of unres content) */
                  if (ly_strequal((const char *)unres->str_snode[i], ident->name, 1)) {
                      LOGVAL(ctx, LYE_INARG, LY_VLOG_NONE, NULL, basename, "base");
                      LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Circular reference of \"%s\" identity.", basename);
                      return -1;
                  }
  
                  return EXIT_FAILURE;
              }
          }
  
          /* checks done, store the result */
          *ret = base;
          return EXIT_SUCCESS;
      }
  
      /* base not found (maybe a forward reference) */
      return EXIT_FAILURE;
  }
  
  /**
   * @brief Resolve base identity. Logs directly.
   *
   * @param[in] module Main module.
   * @param[in] ident Identity to use.
   * @param[in] basename Base name of the identity.
   * @param[in] parent Either "type" or "identity".
   * @param[in,out] type Type structure where we want to resolve identity. Can be NULL.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  static int
  resolve_base_ident(const struct lys_module *module, struct lys_ident *ident, const char *basename, const char *parent,
                     struct lys_type *type, struct unres_schema *unres)
  {
      const char *name;
      int mod_name_len = 0, rc;
      struct lys_ident *target, **ret;
      uint16_t flags;
      struct lys_module *mod;
      struct ly_ctx *ctx = module->ctx;
  
      assert((ident && !type) || (!ident && type));
  
      if (!type) {
          /* have ident to resolve */
          ret = &target;
          flags = ident->flags;
          mod = ident->module;
      } else {
          /* have type to fill */
          ++type->info.ident.count;
          type->info.ident.ref = ly_realloc(type->info.ident.ref, type->info.ident.count * sizeof *type->info.ident.ref);
          LY_CHECK_ERR_RETURN(!type->info.ident.ref, LOGMEM(ctx), -1);
  
          ret = &type->info.ident.ref[type->info.ident.count - 1];
          flags = type->parent->flags;
          mod = type->parent->module;
      }
      *ret = NULL;
  
      /* search for the base identity */
      name = strchr(basename, ':');
      if (name) {
          /* set name to correct position after colon */
          mod_name_len = name - basename;
          name++;
  
          if (!strncmp(basename, module->name, mod_name_len) && !module->name[mod_name_len]) {
              /* prefix refers to the current module, ignore it */
              mod_name_len = 0;
          }
      } else {
          name = basename;
      }
  
      /* get module where to search */
      module = lyp_get_module(module, NULL, 0, mod_name_len ? basename : NULL, mod_name_len, 0);
      if (!module) {
          /* identity refers unknown data model */
          LOGVAL(ctx, LYE_INMOD, LY_VLOG_NONE, NULL, basename);
          return -1;
      }
  
      /* search in the identified module ... */
      rc = resolve_base_ident_sub(module, ident, name, unres, ret);
      if (!rc) {
          assert(*ret);
  
          /* check status */
          if (lyp_check_status(flags, mod, ident ? ident->name : "of type",
                               (*ret)->flags, (*ret)->module, (*ret)->name, NULL)) {
              rc = -1;
          } else if (ident) {
              ident->base[ident->base_size++] = *ret;
              if (lys_main_module(mod)->implemented) {
                  /* in case of the implemented identity, maintain backlinks to it
                   * from the base identities to make it available when resolving
                   * data with the identity values (not implemented identity is not
                   * allowed as an identityref value). */
                  resolve_identity_backlink_update(ident, *ret);
              }
          }
      } else if (rc == EXIT_FAILURE) {
          LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_NONE, NULL, parent, basename);
          if (type) {
              --type->info.ident.count;
          }
      }
  
      return rc;
  }
  
  /*
   * 1 - true (der is derived from base)
   * 0 - false (der is not derived from base)
   */
  static int
  search_base_identity(struct lys_ident *der, struct lys_ident *base)
  {
      int i;
  
      if (der == base) {
          return 1;
      } else {
          for(i = 0; i < der->base_size; i++) {
              if (search_base_identity(der->base[i], base) == 1) {
                  return 1;
              }
          }
      }
  
      return 0;
  }
  
  /**
   * @brief Resolve JSON data format identityref. Logs directly.
   *
   * @param[in] type Identityref type.
   * @param[in] ident_name Identityref name.
   * @param[in] node Node where the identityref is being resolved
   * @param[in] dflt flag if we are resolving default value in the schema
   *
   * @return Pointer to the identity resolvent, NULL on error.
   */
  struct lys_ident *
  resolve_identref(struct lys_type *type, const char *ident_name, struct lyd_node *node, struct lys_module *mod, int dflt)
  {
      const char *mod_name, *name;
      char *str;
      int mod_name_len, nam_len, rc;
      int need_implemented = 0;
      unsigned int i, j, found;
      struct lys_ident *der, *cur;
      struct lys_module *imod = NULL, *m, *tmod;
      struct ly_ctx *ctx;
  
      assert(type && ident_name && mod);
      ctx = mod->ctx;
  
      if (!type || (!type->info.ident.count && !type->der->module) || !ident_name) {
          return NULL;
      }
  
      rc = parse_node_identifier(ident_name, &mod_name, &mod_name_len, &name, &nam_len, NULL, 0);
      if (rc < 1) {
          LOGVAL(ctx, LYE_INCHAR, node ? LY_VLOG_LYD : LY_VLOG_NONE, node, ident_name[-rc], &ident_name[-rc]);
          return NULL;
      } else if (rc < (signed)strlen(ident_name)) {
          LOGVAL(ctx, LYE_INCHAR, node ? LY_VLOG_LYD : LY_VLOG_NONE, node, ident_name[rc], &ident_name[rc]);
          return NULL;
      }
  
      m = lys_main_module(mod); /* shortcut */
      if (!mod_name || (!strncmp(mod_name, m->name, mod_name_len) && !m->name[mod_name_len])) {
          /* identity is defined in the same module as node */
          imod = m;
      } else if (dflt) {
          /* solving identityref in default definition in schema -
           * find the identity's module in the imported modules list to have a correct revision */
          for (i = 0; i < mod->imp_size; i++) {
              if (!strncmp(mod_name, mod->imp[i].module->name, mod_name_len) && !mod->imp[i].module->name[mod_name_len]) {
                  imod = mod->imp[i].module;
                  break;
              }
          }
  
          /* We may need to pull it from the module that the typedef came from */
          if (!imod && type && type->der->module) {
              tmod = type->der->module;
              for (i = 0; i < tmod->imp_size; i++) {
                  if (!strncmp(mod_name, tmod->imp[i].module->name, mod_name_len) && !tmod->imp[i].module->name[mod_name_len]) {
                      imod = tmod->imp[i].module;
                      break;
                  }
              }
          }
  
          /* it can still be a default value in a foreign grouping */
          if (!imod) {
              str = strndup(mod_name, mod_name_len);
              imod = (struct lys_module *)ly_ctx_get_module(ctx, str, NULL, 1);
              free(str);
          }
      } else {
          /* solving identityref in data - get the module from the context */
          for (i = 0; i < (unsigned)mod->ctx->models.used; ++i) {
              imod = mod->ctx->models.list[i];
              if (!strncmp(mod_name, imod->name, mod_name_len) && !imod->name[mod_name_len]) {
                  break;
              }
              imod = NULL;
          }
          if (!imod && mod->ctx->models.parsing_sub_modules_count) {
              /* we are currently parsing some module and checking XPath or a default value,
               * so take this module into account */
              for (i = 0; i < mod->ctx->models.parsing_sub_modules_count; i++) {
                  imod = mod->ctx->models.parsing_sub_modules[i];
                  if (imod->type) {
                      /* skip submodules */
                      continue;
                  }
                  if (!strncmp(mod_name, imod->name, mod_name_len) && !imod->name[mod_name_len]) {
                      break;
                  }
                  imod = NULL;
              }
          }
      }
  
      if (!dflt && (!imod || !imod->implemented) && ctx->data_clb) {
          /* the needed module was not found, but it may have been expected so call the data callback */
          if (imod) {
              ctx->data_clb(ctx, imod->name, imod->ns, LY_MODCLB_NOT_IMPLEMENTED, ctx->data_clb_data);
          } else if (mod_name) {
              str = strndup(mod_name, mod_name_len);
              imod = (struct lys_module *)ctx->data_clb(ctx, str, NULL, 0, ctx->data_clb_data);
              free(str);
          }
      }
      if (!imod) {
          goto fail;
      }
  
      /* find the type with base definitions */
      while (!type->info.ident.count && type->der) {
          type = &type->der->type;
      }
      if (!type->info.ident.count) {
          goto fail;
      }
  
      if (m != imod || lys_main_module(type->parent->module) != mod) {
          /* the type is not referencing the same schema,
           * THEN, we may need to make the module with the identity implemented, but only if it really
           * contains the identity */
          if (!imod->implemented) {
              cur = NULL;
              /* get the identity in the module */
              for (i = 0; i < imod->ident_size; i++) {
                  if (!strcmp(name, imod->ident[i].name)) {
                      cur = &imod->ident[i];
                      break;
                  }
              }
              if (!cur) {
                  /* go through includes */
                  for (j = 0; j < imod->inc_size; j++) {
                      for (i = 0; i < imod->inc[j].submodule->ident_size; i++) {
                          if (!strcmp(name, imod->inc[j].submodule->ident[i].name)) {
                              cur = &imod->inc[j].submodule->ident[i];
                              break;
                          }
                      }
                  }
                  if (!cur) {
                      goto fail;
                  }
              }
  
              /* check that identity is derived from one of the type's base */
              while (type->der) {
                  for (i = 0; i < type->info.ident.count; i++) {
                      if (search_base_identity(cur, type->info.ident.ref[i])) {
                          /* cur's base matches the type's base */
                          need_implemented = 1;
                          goto match;
                      }
                  }
                  type = &type->der->type;
              }
              /* matching base not found */
              LOGVAL(ctx, LYE_SPEC, node ? LY_VLOG_LYD : LY_VLOG_NONE, node, "Identity used as identityref value is not implemented.");
              goto fail;
          }
      }
  
      /* go through all the derived types of all the bases */
      found = 0;
      for (i = 0; i < type->info.ident.count; ++i) {
          cur = type->info.ident.ref[i];
          if (cur->der) {
              /* there are some derived identities */
              for (j = 0; j < cur->der->number; j++) {
                  der = (struct lys_ident *)cur->der->set.g[j]; /* shortcut */
                  if (!strcmp(der->name, name) && lys_main_module(der->module) == imod) {
                      /* we have a match on this base */
                      ++found;
                      break;
                  }
              }
          } else {
              LOGWRN(ctx, "Identity \"%s\" has no derived identities, identityref with this base can never be instatiated.",
                     cur->name);
          }
      }
      if (found == type->info.ident.count) {
          /* match found for all bases */
          cur = der;
          goto match;
      } else {
          LOGVAL(ctx, LYE_SPEC, node ? LY_VLOG_LYD : LY_VLOG_NONE, node, "Identityref value is not derived from all its bases.");
          goto fail;
      }
  
  fail:
      LOGVAL(ctx, LYE_INRESOLV, node ? LY_VLOG_LYD : LY_VLOG_NONE, node, "identityref", ident_name);
      return NULL;
  
  match:
      if (!dflt) {
          for (i = 0; i < cur->iffeature_size; i++) {
              if (!resolve_iffeature(&cur->iffeature[i])) {
                  if (node) {
                      LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, node, cur->name, node->schema->name);
                  }
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Identity \"%s\" is disabled by its %d. if-feature condition.",
                         cur->name, i);
                  return NULL;
              }
          }
      }
      if (need_implemented) {
          if (dflt) {
              /* later try to make the module implemented */
              LOGVRB("Making \"%s\" module implemented because of identityref default value \"%s\" used in the implemented \"%s\" module",
                     imod->name, cur->name, mod->name);
              /* to be more effective we should use UNRES_MOD_IMPLEMENT but that would require changing prototype of
               * several functions with little gain */
              if (lys_set_implemented(imod)) {
                  LOGERR(ctx, ly_errno, "Setting the module \"%s\" implemented because of used default identity \"%s\" failed.",
                         imod->name, cur->name);
                  goto fail;
              }
          } else {
              /* just say that it was found, but in a non-implemented module */
              LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Identity found, but in a non-implemented module \"%s\".",
                     lys_main_module(cur->module)->name);
              goto fail;
          }
      }
      return cur;
  }
  
  /**
   * @brief Resolve unresolved uses. Logs directly.
   *
   * @param[in] uses Uses to use.
   * @param[in] unres Specific unres item.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  static int
  resolve_unres_schema_uses(struct lys_node_uses *uses, struct unres_schema *unres)
  {
      int rc;
      struct lys_node *par_grp;
      struct ly_ctx *ctx = uses->module->ctx;
  
      /* HACK: when a grouping has uses inside, all such uses have to be resolved before the grouping itself is used
       *       in some uses. When we see such a uses, the grouping's unres counter is used to store number of so far
       *       unresolved uses. The grouping cannot be used unless this counter is decreased back to 0. To remember
       *       that the uses already increased grouping's counter, the LYS_USESGRP flag is used. */
      for (par_grp = lys_parent((struct lys_node *)uses); par_grp && (par_grp->nodetype != LYS_GROUPING); par_grp = lys_parent(par_grp));
      if (par_grp && ly_strequal(par_grp->name, uses->name, 1)) {
          LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, uses, "uses", uses->name);
          return -1;
      }
  
      if (!uses->grp) {
          rc = resolve_uses_schema_nodeid(uses->name, (const struct lys_node *)uses, (const struct lys_node_grp **)&uses->grp);
          if (rc == -1) {
              LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, uses, "uses", uses->name);
              return -1;
          } else if (rc > 0) {
              LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYS, uses, uses->name[rc - 1], &uses->name[rc - 1]);
              return -1;
          } else if (!uses->grp) {
              if (par_grp && !(uses->flags & LYS_USESGRP)) {
                  if (++((struct lys_node_grp *)par_grp)->unres_count == 0) {
                      LOGERR(ctx, LY_EINT, "Too many unresolved items (uses) inside a grouping.");
                      return -1;
                  }
                  uses->flags |= LYS_USESGRP;
              }
              LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, uses, "uses", uses->name);
              return EXIT_FAILURE;
          }
      }
  
      if (uses->grp->unres_count) {
          if (par_grp && !(uses->flags & LYS_USESGRP)) {
              if (++((struct lys_node_grp *)par_grp)->unres_count == 0) {
                  LOGERR(ctx, LY_EINT, "Too many unresolved items (uses) inside a grouping.");
                  return -1;
              }
              uses->flags |= LYS_USESGRP;
          } else {
              /* instantiate grouping only when it is completely resolved */
              uses->grp = NULL;
          }
          LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, uses, "uses", uses->name);
          return EXIT_FAILURE;
      }
  
      rc = resolve_uses(uses, unres);
      if (!rc) {
          /* decrease unres count only if not first try */
          if (par_grp && (uses->flags & LYS_USESGRP)) {
              assert(((struct lys_node_grp *)par_grp)->unres_count);
              ((struct lys_node_grp *)par_grp)->unres_count--;
              uses->flags &= ~LYS_USESGRP;
          }
  
          /* check status */
          if (lyp_check_status(uses->flags, uses->module, "of uses",
                           uses->grp->flags, uses->grp->module, uses->grp->name,
                           (struct lys_node *)uses)) {
              return -1;
          }
  
          return EXIT_SUCCESS;
      }
  
      return rc;
  }
  
  /**
   * @brief Resolve list keys. Logs directly.
   *
   * @param[in] list List to use.
   * @param[in] keys_str Keys node value.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  static int
  resolve_list_keys(struct lys_node_list *list, const char *keys_str)
  {
      int i, len, rc;
      const char *value;
      char *s = NULL;
      struct ly_ctx *ctx = list->module->ctx;
  
      for (i = 0; i < list->keys_size; ++i) {
          assert(keys_str);
  
          if (!list->child) {
              /* no child, possible forward reference */
              LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, list, "list keys", keys_str);
              return EXIT_FAILURE;
          }
          /* get the key name */
          if ((value = strpbrk(keys_str, " \t\n"))) {
              len = value - keys_str;
              while (isspace(value[0])) {
                  value++;
              }
          } else {
              len = strlen(keys_str);
          }
  
          rc = lys_getnext_data(lys_node_module((struct lys_node *)list), (struct lys_node *)list, keys_str, len, LYS_LEAF,
                                LYS_GETNEXT_NOSTATECHECK, (const struct lys_node **)&list->keys[i]);
          if (rc) {
              LOGVAL(ctx, LYE_INRESOLV, LY_VLOG_LYS, list, "list key", keys_str);
              return EXIT_FAILURE;
          }
  
          if (check_key(list, i, keys_str, len)) {
              /* check_key logs */
              return -1;
          }
  
          /* check status */
          if (lyp_check_status(list->flags, list->module, list->name,
                               list->keys[i]->flags, list->keys[i]->module, list->keys[i]->name,
                               (struct lys_node *)list->keys[i])) {
              return -1;
          }
  
          /* default value - is ignored, keep it but print a warning */
          if (list->keys[i]->dflt) {
              /* log is not hidden only in case this resolving fails and in such a case
               * we cannot get here
               */
              assert(log_opt == ILO_STORE);
              log_opt = ILO_LOG;
              LOGWRN(ctx, "Default value \"%s\" in the list key \"%s\" is ignored. (%s)", list->keys[i]->dflt,
                     list->keys[i]->name, s = lys_path((struct lys_node*)list, LYS_PATH_FIRST_PREFIX));
              log_opt = ILO_STORE;
              free(s);
          }
  
          /* prepare for next iteration */
          while (value && isspace(value[0])) {
              value++;
          }
          keys_str = value;
      }
  
      return EXIT_SUCCESS;
  }
  
  /**
   * @brief Resolve (check) all must conditions of \p node.
   * Logs directly.
   *
   * @param[in] node Data node with optional must statements.
   * @param[in] inout_parent If set, must in input or output parent of node->schema will be resolved.
   *
   * @return EXIT_SUCCESS on pass, EXIT_FAILURE on fail, -1 on error.
   */
  static int
  resolve_must(struct lyd_node *node, int inout_parent, int ignore_fail)
  {
      uint8_t i, must_size;
      struct lys_node *schema;
      struct lys_restr *must;
      struct lyxp_set set;
      struct ly_ctx *ctx = node->schema->module->ctx;
  
      assert(node);
      memset(&set, 0, sizeof set);
  
      if (inout_parent) {
          for (schema = lys_parent(node->schema);
               schema && (schema->nodetype & (LYS_CHOICE | LYS_CASE | LYS_USES));
               schema = lys_parent(schema));
          if (!schema || !(schema->nodetype & (LYS_INPUT | LYS_OUTPUT))) {
              LOGINT(ctx);
              return -1;
          }
          must_size = ((struct lys_node_inout *)schema)->must_size;
          must = ((struct lys_node_inout *)schema)->must;
  
          /* context node is the RPC/action */
          node = node->parent;
          if (!(node->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
              LOGINT(ctx);
              return -1;
          }
      } else {
          switch (node->schema->nodetype) {
          case LYS_CONTAINER:
              must_size = ((struct lys_node_container *)node->schema)->must_size;
              must = ((struct lys_node_container *)node->schema)->must;
              break;
          case LYS_LEAF:
              must_size = ((struct lys_node_leaf *)node->schema)->must_size;
              must = ((struct lys_node_leaf *)node->schema)->must;
              break;
          case LYS_LEAFLIST:
              must_size = ((struct lys_node_leaflist *)node->schema)->must_size;
              must = ((struct lys_node_leaflist *)node->schema)->must;
              break;
          case LYS_LIST:
              must_size = ((struct lys_node_list *)node->schema)->must_size;
              must = ((struct lys_node_list *)node->schema)->must;
              break;
          case LYS_ANYXML:
          case LYS_ANYDATA:
              must_size = ((struct lys_node_anydata *)node->schema)->must_size;
              must = ((struct lys_node_anydata *)node->schema)->must;
              break;
          case LYS_NOTIF:
              must_size = ((struct lys_node_notif *)node->schema)->must_size;
              must = ((struct lys_node_notif *)node->schema)->must;
              break;
          default:
              must_size = 0;
              break;
          }
      }
  
      for (i = 0; i < must_size; ++i) {
          if (lyxp_eval(must[i].expr, node, LYXP_NODE_ELEM, lyd_node_module(node), &set, LYXP_MUST)) {
              return -1;
          }
  
          lyxp_set_cast(&set, LYXP_SET_BOOLEAN, node, lyd_node_module(node), LYXP_MUST);
  
          if (!set.val.bool) {
              if ((ignore_fail == 1) || ((must[i].flags & (LYS_XPCONF_DEP | LYS_XPSTATE_DEP)) && (ignore_fail == 2))) {
                  LOGVRB("Must condition \"%s\" not satisfied, but it is not required.", must[i].expr);
              } else {
                  LOGVAL(ctx, LYE_NOMUST, LY_VLOG_LYD, node, must[i].expr);
                  if (must[i].emsg) {
                      ly_vlog_str(ctx, LY_VLOG_PREV, must[i].emsg);
                  }
                  if (must[i].eapptag) {
                      ly_err_last_set_apptag(ctx, must[i].eapptag);
                  }
                  return 1;
              }
          }
      }
  
      return EXIT_SUCCESS;
  }
  
  /**
   * @brief Resolve (find) when condition schema context node. Does not log.
   *
   * @param[in] schema Schema node with the when condition.
   * @param[out] ctx_snode When schema context node.
   * @param[out] ctx_snode_type Schema context node type.
   */
  void
  resolve_when_ctx_snode(const struct lys_node *schema, struct lys_node **ctx_snode, enum lyxp_node_type *ctx_snode_type)
  {
      const struct lys_node *sparent;
  
      /* find a not schema-only node */
      *ctx_snode_type = LYXP_NODE_ELEM;
      while (schema->nodetype & (LYS_USES | LYS_CHOICE | LYS_CASE | LYS_AUGMENT | LYS_INPUT | LYS_OUTPUT)) {
          if (schema->nodetype == LYS_AUGMENT) {
              sparent = ((struct lys_node_augment *)schema)->target;
          } else {
              sparent = schema->parent;
          }
          if (!sparent) {
              /* context node is the document root (fake root in our case) */
              if (schema->flags & LYS_CONFIG_W) {
                  *ctx_snode_type = LYXP_NODE_ROOT_CONFIG;
              } else {
                  *ctx_snode_type = LYXP_NODE_ROOT;
              }
              /* we need the first top-level sibling, but no uses or groupings */
              schema = lys_getnext(NULL, NULL, lys_node_module(schema), LYS_GETNEXT_NOSTATECHECK);
              break;
          }
          schema = sparent;
      }
  
      *ctx_snode = (struct lys_node *)schema;
  }
  
  /**
   * @brief Resolve (find) when condition context node. Does not log.
   *
   * @param[in] node Data node, whose conditional definition is being decided.
   * @param[in] schema Schema node with the when condition.
   * @param[out] ctx_node Context node.
   * @param[out] ctx_node_type Context node type.
   *
   * @return EXIT_SUCCESS on success, -1 on error.
   */
  static int
  resolve_when_ctx_node(struct lyd_node *node, struct lys_node *schema, struct lyd_node **ctx_node,
                        enum lyxp_node_type *ctx_node_type)
  {
      struct lyd_node *parent;
      struct lys_node *sparent;
      enum lyxp_node_type node_type;
      uint16_t i, data_depth, schema_depth;
  
      resolve_when_ctx_snode(schema, &schema, &node_type);
  
      if (node_type == LYXP_NODE_ELEM) {
          /* standard element context node */
          for (parent = node, data_depth = 0; parent; parent = parent->parent, ++data_depth);
          for (sparent = schema, schema_depth = 0;
                  sparent;
                  sparent = (sparent->nodetype == LYS_AUGMENT ? ((struct lys_node_augment *)sparent)->target : sparent->parent)) {
              if (sparent->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_ANYDATA | LYS_NOTIF | LYS_RPC)) {
                  ++schema_depth;
              }
          }
          if (data_depth < schema_depth) {
              return -1;
          }
  
          /* find the corresponding data node */
          for (i = 0; i < data_depth - schema_depth; ++i) {
              node = node->parent;
          }
          if (node->schema != schema) {
              return -1;
          }
      } else {
          /* root context node */
          while (node->parent) {
              node = node->parent;
          }
          while (node->prev->next) {
              node = node->prev;
          }
      }
  
      *ctx_node = node;
      *ctx_node_type = node_type;
      return EXIT_SUCCESS;
  }
  
  /**
   * @brief Temporarily unlink nodes as per YANG 1.1 RFC section 7.21.5 for when XPath evaluation.
   * The context node is adjusted if needed.
   *
   * @param[in] snode Schema node, whose children instances need to be unlinked.
   * @param[in,out] node Data siblings where to look for the children of \p snode. If it is unlinked,
   * it is moved to point to another sibling still in the original tree.
   * @param[in,out] ctx_node When context node, adjusted if needed.
   * @param[in] ctx_node_type Context node type, just for information to detect invalid situations.
   * @param[out] unlinked_nodes Unlinked siblings. Can be safely appended to \p node afterwards.
   * Ordering may change, but there will be no semantic change.
   *
   * @return EXIT_SUCCESS on success, -1 on error.
   */
  static int
  resolve_when_unlink_nodes(struct lys_node *snode, struct lyd_node **node, struct lyd_node **ctx_node,
                            enum lyxp_node_type ctx_node_type, struct lyd_node **unlinked_nodes)
  {
      struct lyd_node *next, *elem;
      const struct lys_node *slast;
      struct ly_ctx *ctx = snode->module->ctx;
  
      switch (snode->nodetype) {
      case LYS_AUGMENT:
      case LYS_USES:
      case LYS_CHOICE:
      case LYS_CASE:
          slast = NULL;
          while ((slast = lys_getnext(slast, snode, NULL, LYS_GETNEXT_PARENTUSES))) {
              if (slast->nodetype & (LYS_ACTION | LYS_NOTIF)) {
                  continue;
              }
  
              if (resolve_when_unlink_nodes((struct lys_node *)slast, node, ctx_node, ctx_node_type, unlinked_nodes)) {
                  return -1;
              }
          }
          break;
      case LYS_CONTAINER:
      case LYS_LIST:
      case LYS_LEAF:
      case LYS_LEAFLIST:
      case LYS_ANYXML:
      case LYS_ANYDATA:
          LY_TREE_FOR_SAFE(lyd_first_sibling(*node), next, elem) {
              if (elem->schema == snode) {
  
                  if (elem == *ctx_node) {
                      /* We are going to unlink our context node! This normally cannot happen,
                       * but we use normal top-level data nodes for faking a document root node,
                       * so if this is the context node, we just use the next top-level node.
                       * Additionally, it can even happen that there are no top-level data nodes left,
                       * all were unlinked, so in this case we pass NULL as the context node/data tree,
                       * lyxp_eval() can handle this special situation.
                       */
                      if (ctx_node_type == LYXP_NODE_ELEM) {
                          LOGINT(ctx);
                          return -1;
                      }
  
                      if (elem->prev == elem) {
                          /* unlinking last top-level element, use an empty data tree */
                          *ctx_node = NULL;
                      } else {
                          /* in this case just use the previous/last top-level data node */
                          *ctx_node = elem->prev;
                      }
                  } else if (elem == *node) {
                      /* We are going to unlink the currently processed node. This does not matter that
                       * much, but we would lose access to the original data tree, so just move our
                       * pointer somewhere still inside it.
                       */
                      if ((*node)->prev != *node) {
                          *node = (*node)->prev;
                      } else {
                          /* the processed node with sibings were all unlinked, oh well */
                          *node = NULL;
                      }
                  }
  
                  /* temporarily unlink the node */
                  lyd_unlink_internal(elem, 0);
                  if (*unlinked_nodes) {
                      if (lyd_insert_after((*unlinked_nodes)->prev, elem)) {
                          LOGINT(ctx);
                          return -1;
                      }
                  } else {
                      *unlinked_nodes = elem;
                  }
  
                  if (snode->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_ANYDATA)) {
                      /* there can be only one instance */
                      break;
                  }
              }
          }
          break;
      default:
          LOGINT(ctx);
          return -1;
      }
  
      return EXIT_SUCCESS;
  }
  
  /**
   * @brief Relink the unlinked nodes back.
   *
   * @param[in] node Data node to link the nodes back to. It can actually be the adjusted context node,
   * we simply need a sibling from the original data tree.
   * @param[in] unlinked_nodes Unlinked nodes to relink to \p node.
   * @param[in] ctx_node_type Context node type to distinguish between \p node being the parent
   * or the sibling of \p unlinked_nodes.
   *
   * @return EXIT_SUCCESS on success, -1 on error.
   */
  static int
  resolve_when_relink_nodes(struct lyd_node *node, struct lyd_node *unlinked_nodes, enum lyxp_node_type ctx_node_type)
  {
      struct lyd_node *elem;
  
      LY_TREE_FOR_SAFE(unlinked_nodes, unlinked_nodes, elem) {
          lyd_unlink_internal(elem, 0);
          if (ctx_node_type == LYXP_NODE_ELEM) {
              if (lyd_insert_common(node, NULL, elem, 0)) {
                  return -1;
              }
          } else {
              if (lyd_insert_nextto(node, elem, 0, 0)) {
                  return -1;
              }
          }
      }
  
      return EXIT_SUCCESS;
  }
  
  int
  resolve_applies_must(const struct lyd_node *node)
  {
      int ret = 0;
      uint8_t must_size;
      struct lys_node *schema, *iter;
  
      assert(node);
  
      schema = node->schema;
  
      /* their own must */
      switch (schema->nodetype) {
      case LYS_CONTAINER:
          must_size = ((struct lys_node_container *)schema)->must_size;
          break;
      case LYS_LEAF:
          must_size = ((struct lys_node_leaf *)schema)->must_size;
          break;
      case LYS_LEAFLIST:
          must_size = ((struct lys_node_leaflist *)schema)->must_size;
          break;
      case LYS_LIST:
          must_size = ((struct lys_node_list *)schema)->must_size;
          break;
      case LYS_ANYXML:
      case LYS_ANYDATA:
          must_size = ((struct lys_node_anydata *)schema)->must_size;
          break;
      case LYS_NOTIF:
          must_size = ((struct lys_node_notif *)schema)->must_size;
          break;
      default:
          must_size = 0;
          break;
      }
  
      if (must_size) {
          ++ret;
      }
  
      /* schema may be a direct data child of input/output with must (but it must be first, it needs to be evaluated only once) */
      if (!node->prev->next) {
          for (iter = lys_parent(schema); iter && (iter->nodetype & (LYS_CHOICE | LYS_CASE | LYS_USES)); iter = lys_parent(iter));
          if (iter && (iter->nodetype & (LYS_INPUT | LYS_OUTPUT))) {
              ret += 0x2;
          }
      }
  
      return ret;
  }
  
  static struct lys_when *
  snode_get_when(const struct lys_node *schema)
  {
      switch (schema->nodetype) {
      case LYS_CONTAINER:
          return ((struct lys_node_container *)schema)->when;
      case LYS_CHOICE:
          return ((struct lys_node_choice *)schema)->when;
      case LYS_LEAF:
          return ((struct lys_node_leaf *)schema)->when;
      case LYS_LEAFLIST:
          return ((struct lys_node_leaflist *)schema)->when;
      case LYS_LIST:
          return ((struct lys_node_list *)schema)->when;
      case LYS_ANYDATA:
      case LYS_ANYXML:
          return ((struct lys_node_anydata *)schema)->when;
      case LYS_CASE:
          return ((struct lys_node_case *)schema)->when;
      case LYS_USES:
          return ((struct lys_node_uses *)schema)->when;
      case LYS_AUGMENT:
          return ((struct lys_node_augment *)schema)->when;
      default:
          return NULL;
      }
  }
  
  int
  resolve_applies_when(const struct lys_node *schema, int mode, const struct lys_node *stop)
  {
      const struct lys_node *parent;
  
      assert(schema);
  
      if (!(schema->nodetype & (LYS_NOTIF | LYS_RPC)) && snode_get_when(schema)) {
          return 1;
      }
  
      parent = schema;
      goto check_augment;
  
      while (parent) {
          /* stop conditions */
          if (!mode) {
              /* stop on node that can be instantiated in data tree */
              if (!(parent->nodetype & (LYS_USES | LYS_CHOICE | LYS_CASE))) {
                  break;
              }
          } else {
              /* stop on the specified node */
              if (parent == stop) {
                  break;
              }
          }
  
          if (snode_get_when(parent)) {
              return 1;
          }
  check_augment:
  
          if (parent->parent && (parent->parent->nodetype == LYS_AUGMENT) && snode_get_when(parent->parent)) {
              return 1;
          }
          parent = lys_parent(parent);
      }
  
      return 0;
  }
  
  /**
   * @brief Resolve (check) all when conditions relevant for \p node.
   * Logs directly.
   *
   * @param[in] node Data node, whose conditional reference, if such, is being decided.
   * @param[in] ignore_fail 1 if when does not have to be satisfied, 2 if it does not have to be satisfied
   * only when requiring external dependencies.
   *
   * @return
   *  -1 - error, ly_errno is set
   *   0 - all "when" statements true
   *   0, ly_vecode = LYVE_NOWHEN - some "when" statement false, returned in failed_when
   *   1, ly_vecode = LYVE_INWHEN - nodes needed to resolve are conditional and not yet resolved (under another "when")
   */
  int
  resolve_when(struct lyd_node *node, int ignore_fail, struct lys_when **failed_when)
  {
      struct lyd_node *ctx_node = NULL, *unlinked_nodes, *tmp_node;
      struct lys_node *sparent;
      struct lyxp_set set;
      enum lyxp_node_type ctx_node_type;
      struct ly_ctx *ctx = node->schema->module->ctx;
      int rc = 0;
  
      assert(node);
      memset(&set, 0, sizeof set);
  
      if (!(node->schema->nodetype & (LYS_NOTIF | LYS_RPC | LYS_ACTION)) && snode_get_when(node->schema)) {
          /* make the node dummy for the evaluation */
          node->validity |= LYD_VAL_INUSE;
          rc = lyxp_eval(snode_get_when(node->schema)->cond, node, LYXP_NODE_ELEM, lyd_node_module(node),
                         &set, LYXP_WHEN);
          node->validity &= ~LYD_VAL_INUSE;
          if (rc) {
              if (rc == 1) {
                  LOGVAL(ctx, LYE_INWHEN, LY_VLOG_LYD, node, snode_get_when(node->schema)->cond);
              }
              goto cleanup;
          }
  
          /* set boolean result of the condition */
          lyxp_set_cast(&set, LYXP_SET_BOOLEAN, node, lyd_node_module(node), LYXP_WHEN);
          if (!set.val.bool) {
              node->when_status |= LYD_WHEN_FALSE;
              if ((ignore_fail == 1) || ((snode_get_when(node->schema)->flags & (LYS_XPCONF_DEP | LYS_XPSTATE_DEP))
                      && (ignore_fail == 2))) {
                  LOGVRB("When condition \"%s\" is not satisfied, but it is not required.", snode_get_when(node->schema)->cond);
              } else {
                  LOGVAL(ctx, LYE_NOWHEN, LY_VLOG_LYD, node, snode_get_when(node->schema)->cond);
                  if (failed_when) {
                      *failed_when = snode_get_when(node->schema);
                  }
                  goto cleanup;
              }
          }
  
          /* free xpath set content */
          lyxp_set_cast(&set, LYXP_SET_EMPTY, node, lyd_node_module(node), 0);
      }
  
      sparent = node->schema;
      goto check_augment;
  
      /* check when in every schema node that affects node */
      while (sparent && (sparent->nodetype & (LYS_USES | LYS_CHOICE | LYS_CASE))) {
          if (snode_get_when(sparent)) {
              if (!ctx_node) {
                  rc = resolve_when_ctx_node(node, sparent, &ctx_node, &ctx_node_type);
                  if (rc) {
                      LOGINT(ctx);
                      goto cleanup;
                  }
              }
  
              unlinked_nodes = NULL;
              /* we do not want our node pointer to change */
              tmp_node = node;
              rc = resolve_when_unlink_nodes(sparent, &tmp_node, &ctx_node, ctx_node_type, &unlinked_nodes);
              if (rc) {
                  goto cleanup;
              }
  
              rc = lyxp_eval(snode_get_when(sparent)->cond, ctx_node, ctx_node_type, lys_node_module(sparent),
                             &set, LYXP_WHEN);
  
              if (unlinked_nodes && ctx_node) {
                  if (resolve_when_relink_nodes(ctx_node, unlinked_nodes, ctx_node_type)) {
                      rc = -1;
                      goto cleanup;
                  }
              }
  
              if (rc) {
                  if (rc == 1) {
                      LOGVAL(ctx, LYE_INWHEN, LY_VLOG_LYD, node, snode_get_when(sparent)->cond);
                  }
                  goto cleanup;
              }
  
              lyxp_set_cast(&set, LYXP_SET_BOOLEAN, ctx_node, lys_node_module(sparent), LYXP_WHEN);
              if (!set.val.bool) {
                  if ((ignore_fail == 1) || ((snode_get_when(sparent)->flags & (LYS_XPCONF_DEP | LYS_XPSTATE_DEP))
                          && (ignore_fail == 2))) {
                      LOGVRB("When condition \"%s\" is not satisfied, but it is not required.", snode_get_when(sparent)->cond);
                  } else {
                      node->when_status |= LYD_WHEN_FALSE;
                      LOGVAL(ctx, LYE_NOWHEN, LY_VLOG_LYD, node, snode_get_when(sparent)->cond);
                      if (failed_when) {
                          *failed_when = snode_get_when(sparent);
                      }
                      goto cleanup;
                  }
              }
  
              /* free xpath set content */
              lyxp_set_cast(&set, LYXP_SET_EMPTY, ctx_node, lys_node_module(sparent), 0);
          }
  
  check_augment:
          if ((sparent->parent && (sparent->parent->nodetype == LYS_AUGMENT) && snode_get_when(sparent->parent))) {
              if (!ctx_node) {
                  rc = resolve_when_ctx_node(node, sparent->parent, &ctx_node, &ctx_node_type);
                  if (rc) {
                      LOGINT(ctx);
                      goto cleanup;
                  }
              }
  
              unlinked_nodes = NULL;
              tmp_node = node;
              rc = resolve_when_unlink_nodes(sparent->parent, &tmp_node, &ctx_node, ctx_node_type, &unlinked_nodes);
              if (rc) {
                  goto cleanup;
              }
  
              rc = lyxp_eval(snode_get_when(sparent->parent)->cond, ctx_node, ctx_node_type,
                             lys_node_module(sparent->parent), &set, LYXP_WHEN);
  
              /* reconnect nodes, if ctx_node is NULL then all the nodes were unlinked, but linked together,
               * so the tree did not actually change and there is nothing for us to do
               */
              if (unlinked_nodes && ctx_node) {
                  if (resolve_when_relink_nodes(ctx_node, unlinked_nodes, ctx_node_type)) {
                      rc = -1;
                      goto cleanup;
                  }
              }
  
              if (rc) {
                  if (rc == 1) {
                      LOGVAL(ctx, LYE_INWHEN, LY_VLOG_LYD, node, snode_get_when(sparent->parent)->cond);
                  }
                  goto cleanup;
              }
  
              lyxp_set_cast(&set, LYXP_SET_BOOLEAN, ctx_node, lys_node_module(sparent->parent), LYXP_WHEN);
              if (!set.val.bool) {
                  node->when_status |= LYD_WHEN_FALSE;
                  if ((ignore_fail == 1) || ((snode_get_when(sparent->parent)->flags & (LYS_XPCONF_DEP | LYS_XPSTATE_DEP))
                          && (ignore_fail == 2))) {
                      LOGVRB("When condition \"%s\" is not satisfied, but it is not required.",
                             snode_get_when(sparent->parent)->cond);
                  } else {
                      LOGVAL(ctx, LYE_NOWHEN, LY_VLOG_LYD, node, snode_get_when(sparent->parent)->cond);
                      if (failed_when) {
                          *failed_when = snode_get_when(sparent->parent);
                      }
                      goto cleanup;
                  }
              }
  
              /* free xpath set content */
              lyxp_set_cast(&set, LYXP_SET_EMPTY, ctx_node, lys_node_module(sparent->parent), 0);
          }
  
          sparent = lys_parent(sparent);
      }
  
      node->when_status |= LYD_WHEN_TRUE;
  
  cleanup:
      /* free xpath set content */
      lyxp_set_cast(&set, LYXP_SET_EMPTY, ctx_node ? ctx_node : node, NULL, 0);
      return rc;
  }
  
  static int
  check_type_union_leafref(struct lys_type *type)
  {
      uint8_t i;
  
      if ((type->base == LY_TYPE_UNION) && type->info.uni.count) {
          /* go through unions and look for leafref */
          for (i = 0; i < type->info.uni.count; ++i) {
              switch (type->info.uni.types[i].base) {
              case LY_TYPE_LEAFREF:
                  return 1;
              case LY_TYPE_UNION:
                  if (check_type_union_leafref(&type->info.uni.types[i])) {
                      return 1;
                  }
                  break;
              default:
                  break;
              }
          }
  
          return 0;
      }
  
      /* just inherit the flag value */
      return type->der->has_union_leafref;
  }
  
  /**
   * @brief Resolve a single unres schema item. Logs indirectly.
   *
   * @param[in] mod Main module.
   * @param[in] item Item to resolve. Type determined by \p type.
   * @param[in] type Type of the unresolved item.
   * @param[in] str_snode String, a schema node, or NULL.
   * @param[in] unres Unres schema structure to use.
   * @param[in] final_fail Whether we are just printing errors of the failed unres items.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  static int
  resolve_unres_schema_item(struct lys_module *mod, void *item, enum UNRES_ITEM type, void *str_snode,
                            struct unres_schema *unres)
  {
      /* has_str - whether the str_snode is a string in a dictionary that needs to be freed */
      int rc = -1, has_str = 0, parent_type = 0, i, k;
      unsigned int j;
      struct ly_ctx * ctx = mod->ctx;
      struct lys_node *root, *next, *node, *par_grp;
      const char *expr;
      uint8_t *u;
  
      struct ly_set *refs, *procs;
      struct lys_feature *ref, *feat;
      struct lys_ident *ident;
      struct lys_type *stype;
      struct lys_node_choice *choic;
      struct lyxml_elem *yin;
      struct yang_type *yang;
      struct unres_list_uniq *unique_info;
      struct unres_iffeat_data *iff_data;
      struct unres_ext *ext_data;
      struct lys_ext_instance *ext, **extlist;
      struct lyext_plugin *eplugin;
  
      switch (type) {
      case UNRES_IDENT:
          expr = str_snode;
          has_str = 1;
          ident = item;
  
          rc = resolve_base_ident(mod, ident, expr, "identity", NULL, unres);
          break;
      case UNRES_TYPE_IDENTREF:
          expr = str_snode;
          has_str = 1;
          stype = item;
  
          rc = resolve_base_ident(mod, NULL, expr, "type", stype, unres);
          break;
      case UNRES_TYPE_LEAFREF:
          node = str_snode;
          stype = item;
  
          rc = resolve_schema_leafref(stype, node, unres);
          break;
      case UNRES_TYPE_DER_EXT:
          parent_type++;
          /* falls through */
      case UNRES_TYPE_DER_TPDF:
          parent_type++;
          /* falls through */
      case UNRES_TYPE_DER:
          /* parent */
          node = str_snode;
          stype = item;
  
          /* HACK type->der is temporarily unparsed type statement */
          yin = (struct lyxml_elem *)stype->der;
          stype->der = NULL;
  
          if (yin->flags & LY_YANG_STRUCTURE_FLAG) {
              yang = (struct yang_type *)yin;
              rc = yang_check_type(mod, node, yang, stype, parent_type, unres);
  
              if (rc) {
                  /* may try again later */
                  stype->der = (struct lys_tpdf *)yang;
              } else {
                  /* we need to always be able to free this, it's safe only in this case */
                  lydict_remove(ctx, yang->name);
                  free(yang);
              }
  
          } else {
              rc = fill_yin_type(mod, node, yin, stype, parent_type, unres);
              if (!rc || rc == -1) {
                  /* we need to always be able to free this, it's safe only in this case */
                  lyxml_free(ctx, yin);
              } else {
                  /* may try again later, put all back how it was */
                  stype->der = (struct lys_tpdf *)yin;
              }
          }
          if (rc == EXIT_SUCCESS) {
              /* it does not make sense to have leaf-list of empty type */
              if (!parent_type && node->nodetype == LYS_LEAFLIST && stype->base == LY_TYPE_EMPTY) {
                  LOGWRN(ctx, "The leaf-list \"%s\" is of \"empty\" type, which does not make sense.", node->name);
              }
  
              if ((type == UNRES_TYPE_DER_TPDF) && (stype->base == LY_TYPE_UNION)) {
                  /* fill typedef union leafref flag */
                  ((struct lys_tpdf *)stype->parent)->has_union_leafref = check_type_union_leafref(stype);
              } else if ((type == UNRES_TYPE_DER) && stype->der->has_union_leafref) {
                  /* copy the type in case it has union leafref flag */
                  if (lys_copy_union_leafrefs(mod, node, stype, NULL, unres)) {
                      LOGERR(ctx, LY_EINT, "Failed to duplicate type.");
                      return -1;
                  }
              }
          } else if (rc == EXIT_FAILURE && !(stype->value_flags & LY_VALUE_UNRESGRP)) {
              /* forward reference - in case the type is in grouping, we have to make the grouping unusable
               * by uses statement until the type is resolved. We do that the same way as uses statements inside
               * grouping. The grouping cannot be used unless the unres counter is 0.
               * To remember that the grouping already increased the counter, the LYTYPE_GRP is used as value
               * of the type's base member. */
              for (par_grp = node; par_grp && (par_grp->nodetype != LYS_GROUPING); par_grp = lys_parent(par_grp));
              if (par_grp) {
                  if (++((struct lys_node_grp *)par_grp)->unres_count == 0) {
                      LOGERR(ctx, LY_EINT, "Too many unresolved items (type) inside a grouping.");
                      return -1;
                  }
                  stype->value_flags |= LY_VALUE_UNRESGRP;
              }
          }
          break;
      case UNRES_IFFEAT:
          iff_data = str_snode;
          rc = resolve_feature(iff_data->fname, strlen(iff_data->fname), iff_data->node, item);
          if (!rc) {
              /* success */
              if (iff_data->infeature) {
                  /* store backlink into the target feature to allow reverse changes in case of changing feature status */
                  feat = *((struct lys_feature **)item);
                  if (!feat->depfeatures) {
                      feat->depfeatures = ly_set_new();
                  }
                  ly_set_add(feat->depfeatures, iff_data->node, LY_SET_OPT_USEASLIST);
              }
              /* cleanup temporary data */
              lydict_remove(ctx, iff_data->fname);
              free(iff_data);
          }
          break;
      case UNRES_FEATURE:
          feat = (struct lys_feature *)item;
  
          if (feat->iffeature_size) {
              refs = ly_set_new();
              procs = ly_set_new();
              ly_set_add(procs, feat, 0);
  
              while (procs->number) {
                  ref = procs->set.g[procs->number - 1];
                  ly_set_rm_index(procs, procs->number - 1);
  
                  for (i = 0; i < ref->iffeature_size; i++) {
                      resolve_iffeature_getsizes(&ref->iffeature[i], NULL, &j);
                      for (; j > 0 ; j--) {
                          if (ref->iffeature[i].features[j - 1]) {
                              if (ref->iffeature[i].features[j - 1] == feat) {
                                  LOGVAL(ctx, LYE_CIRC_FEATURES, LY_VLOG_NONE, NULL, feat->name);
                                  goto featurecheckdone;
                              }
  
                              if (ref->iffeature[i].features[j - 1]->iffeature_size) {
                                  k = refs->number;
                                  if (ly_set_add(refs, ref->iffeature[i].features[j - 1], 0) == k) {
                                      /* not yet seen feature, add it for processing */
                                      ly_set_add(procs, ref->iffeature[i].features[j - 1], 0);
                                  }
                              }
                          } else {
                              /* forward reference */
                              rc = EXIT_FAILURE;
                              goto featurecheckdone;
                          }
                      }
  
                  }
              }
              rc = EXIT_SUCCESS;
  
  featurecheckdone:
              ly_set_free(refs);
              ly_set_free(procs);
          }
  
          break;
      case UNRES_USES:
          rc = resolve_unres_schema_uses(item, unres);
          break;
      case UNRES_TYPEDEF_DFLT:
          parent_type++;
          /* falls through */
      case UNRES_TYPE_DFLT:
          stype = item;
          rc = check_default(stype, (const char **)str_snode, mod, parent_type);
          if ((rc == EXIT_FAILURE) && !parent_type && (stype->base == LY_TYPE_LEAFREF)) {
              for (par_grp = (struct lys_node *)stype->parent;
                   par_grp && (par_grp->nodetype != LYS_GROUPING);
                   par_grp = lys_parent(par_grp));
              if (par_grp) {
                  /* checking default value in a grouping finished with forward reference means we cannot check the value */
                  rc = EXIT_SUCCESS;
              }
          }
          break;
      case UNRES_CHOICE_DFLT:
          expr = str_snode;
          has_str = 1;
          choic = item;
  
          if (!choic->dflt) {
              choic->dflt = resolve_choice_dflt(choic, expr);
          }
          if (choic->dflt) {
              rc = lyp_check_mandatory_choice((struct lys_node *)choic);
          } else {
              rc = EXIT_FAILURE;
          }
          break;
      case UNRES_LIST_KEYS:
          rc = resolve_list_keys(item, ((struct lys_node_list *)item)->keys_str);
          break;
      case UNRES_LIST_UNIQ:
          unique_info = (struct unres_list_uniq *)item;
          rc = resolve_unique(unique_info->list, unique_info->expr, unique_info->trg_type);
          break;
      case UNRES_AUGMENT:
          rc = resolve_augment(item, NULL, unres);
          break;
      case UNRES_XPATH:
          node = (struct lys_node *)item;
          rc = check_xpath(node, 1);
          break;
      case UNRES_MOD_IMPLEMENT:
          rc = lys_make_implemented_r(mod, unres);
          break;
      case UNRES_EXT:
          ext_data = (struct unres_ext *)str_snode;
          extlist = &(*(struct lys_ext_instance ***)item)[ext_data->ext_index];
          rc = resolve_extension(ext_data, extlist, unres);
          if (!rc) {
              /* success */
              /* is there a callback to be done to finalize the extension? */
              eplugin = extlist[0]->def->plugin;
              if (eplugin) {
                  if (eplugin->check_result || (eplugin->flags & LYEXT_OPT_INHERIT)) {
                      u = malloc(sizeof *u);
                      LY_CHECK_ERR_RETURN(!u, LOGMEM(ctx), -1);
                      (*u) = ext_data->ext_index;
                      if (unres_schema_add_node(mod, unres, item, UNRES_EXT_FINALIZE, (struct lys_node *)u) == -1) {
                          /* something really bad happened since the extension finalization is not actually
                           * being resolved while adding into unres, so something more serious with the unres
                           * list itself must happened */
                          return -1;
                      }
                  }
              }
          }
          if (!rc || rc == -1) {
              /* cleanup on success or fatal error */
              if (ext_data->datatype == LYS_IN_YIN) {
                  /* YIN */
                  lyxml_free(ctx, ext_data->data.yin);
              } else {
                  /* YANG */
                  yang_free_ext_data(ext_data->data.yang);
              }
              free(ext_data);
          }
          break;
      case UNRES_EXT_FINALIZE:
          u = (uint8_t *)str_snode;
          ext = (*(struct lys_ext_instance ***)item)[*u];
          free(u);
  
          eplugin = ext->def->plugin;
  
          /* inherit */
          if ((eplugin->flags & LYEXT_OPT_INHERIT) && (ext->parent_type == LYEXT_PAR_NODE)) {
              root = (struct lys_node *)ext->parent;
              if (!(root->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) {
                  LY_TREE_DFS_BEGIN(root->child, next, node) {
                      /* first, check if the node already contain instance of the same extension,
                       * in such a case we won't inherit. In case the node was actually defined as
                       * augment data, we are supposed to check the same way also the augment node itself */
                      if (lys_ext_instance_presence(ext->def, node->ext, node->ext_size) != -1) {
                          goto inherit_dfs_sibling;
                      } else if (node->parent != root && node->parent->nodetype == LYS_AUGMENT &&
                              lys_ext_instance_presence(ext->def, node->parent->ext, node->parent->ext_size) != -1) {
                          goto inherit_dfs_sibling;
                      }
  
                      if (eplugin->check_inherit) {
                          /* we have a callback to check the inheritance, use it */
                          switch ((rc = (*eplugin->check_inherit)(ext, node))) {
                          case 0:
                              /* yes - continue with the inheriting code */
                              break;
                          case 1:
                              /* no - continue with the node's sibling */
                              goto inherit_dfs_sibling;
                          case 2:
                              /* no, but continue with the children, just skip the inheriting code for this node */
                              goto inherit_dfs_child;
                          default:
                              LOGERR(ctx, LY_EINT, "Plugin's (%s:%s) check_inherit callback returns invalid value (%d),",
                                     ext->def->module->name, ext->def->name, rc);
                          }
                      }
  
                      /* inherit the extension */
                      extlist = realloc(node->ext, (node->ext_size + 1) * sizeof *node->ext);
                      LY_CHECK_ERR_RETURN(!extlist, LOGMEM(ctx), -1);
                      extlist[node->ext_size] = malloc(sizeof **extlist);
                      LY_CHECK_ERR_RETURN(!extlist[node->ext_size], LOGMEM(ctx); node->ext = extlist, -1);
                      memcpy(extlist[node->ext_size], ext, sizeof *ext);
                      extlist[node->ext_size]->flags |= LYEXT_OPT_INHERIT;
  
                      node->ext = extlist;
                      node->ext_size++;
  
  inherit_dfs_child:
                      /* modification of - select element for the next run - children first */
                      if (node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) {
                          next = NULL;
                      } else {
                          next = node->child;
                      }
                      if (!next) {
  inherit_dfs_sibling:
                          /* no children, try siblings */
                          next = node->next;
                      }
                      while (!next) {
                          /* go to the parent */
                          node = lys_parent(node);
  
                          /* we are done if we are back in the root (the starter's parent */
                          if (node == root) {
                              break;
                          }
  
                          /* parent is already processed, go to its sibling */
                          next = node->next;
                      }
                  }
              }
          }
  
          /* final check */
          if (eplugin->check_result) {
              if ((*eplugin->check_result)(ext)) {
                  LOGERR(ctx, LY_EPLUGIN, "Resolving extension failed.");
                  return -1;
              }
          }
  
          rc = 0;
          break;
      default:
          LOGINT(ctx);
          break;
      }
  
      if (has_str && !rc) {
          /* the string is no more needed in case of success.
           * In case of forward reference, we will try to resolve the string later */
          lydict_remove(ctx, str_snode);
      }
  
      return rc;
  }
  
  /* logs directly */
  static void
  print_unres_schema_item_fail(void *item, enum UNRES_ITEM type, void *str_node)
  {
      struct lyxml_elem *xml;
      struct lyxml_attr *attr;
      struct unres_iffeat_data *iff_data;
      const char *name = NULL;
      struct unres_ext *extinfo;
  
      switch (type) {
      case UNRES_IDENT:
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "identity", (char *)str_node);
          break;
      case UNRES_TYPE_IDENTREF:
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "identityref", (char *)str_node);
          break;
      case UNRES_TYPE_LEAFREF:
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "leafref",
                 ((struct lys_type *)item)->info.lref.path);
          break;
      case UNRES_TYPE_DER_EXT:
      case UNRES_TYPE_DER_TPDF:
      case UNRES_TYPE_DER:
          xml = (struct lyxml_elem *)((struct lys_type *)item)->der;
          if (xml->flags & LY_YANG_STRUCTURE_FLAG) {
              name = ((struct yang_type *)xml)->name;
          } else {
              LY_TREE_FOR(xml->attr, attr) {
                  if ((attr->type == LYXML_ATTR_STD) && !strcmp(attr->name, "name")) {
                      name = attr->value;
                      break;
                  }
              }
              assert(attr);
          }
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "derived type", name);
          break;
      case UNRES_IFFEAT:
          iff_data = str_node;
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "if-feature", iff_data->fname);
          break;
      case UNRES_FEATURE:
          LOGVRB("There are unresolved if-features for \"%s\" feature circular dependency check, it will be attempted later",
                 ((struct lys_feature *)item)->name);
          break;
      case UNRES_USES:
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "uses", ((struct lys_node_uses *)item)->name);
          break;
      case UNRES_TYPEDEF_DFLT:
      case UNRES_TYPE_DFLT:
          if (*(char **)str_node) {
              LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "type default", *(char **)str_node);
          }   /* else no default value in the type itself, but we are checking some restrictions against
               *  possible default value of some base type. The failure is caused by not resolved base type,
               *  so it was already reported */
          break;
      case UNRES_CHOICE_DFLT:
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "choice default", (char *)str_node);
          break;
      case UNRES_LIST_KEYS:
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "list keys", (char *)str_node);
          break;
      case UNRES_LIST_UNIQ:
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "list unique", (char *)str_node);
          break;
      case UNRES_AUGMENT:
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "augment target",
                 ((struct lys_node_augment *)item)->target_name);
          break;
      case UNRES_XPATH:
          LOGVRB("Resolving %s \"%s\" failed, it will be attempted later.", "XPath expressions of",
                 ((struct lys_node *)item)->name);
          break;
      case UNRES_EXT:
          extinfo = (struct unres_ext *)str_node;
          name = extinfo->datatype == LYS_IN_YIN ? extinfo->data.yin->name : NULL; /* TODO YANG extension */
          LOGVRB("Resolving extension \"%s\" failed, it will be attempted later.", name);
          break;
      default:
          LOGINT(NULL);
          break;
      }
  }
  
  static int
  resolve_unres_schema_types(struct unres_schema *unres, enum UNRES_ITEM types, struct ly_ctx *ctx, int forward_ref,
                             int print_all_errors, uint32_t *resolved)
  {
      uint32_t i, unres_count, res_count;
      int ret = 0, rc;
      struct ly_err_item *prev_eitem;
      enum int_log_opts prev_ilo;
      LY_ERR prev_ly_errno;
  
      /* if there can be no forward references, every failure is final, so we can print it directly */
      if (forward_ref) {
          prev_ly_errno = ly_errno;
          ly_ilo_change(ctx, ILO_STORE, &prev_ilo, &prev_eitem);
      }
  
      do {
          unres_count = 0;
          res_count = 0;
  
          for (i = 0; i < unres->count; ++i) {
              /* UNRES_TYPE_LEAFREF must be resolved (for storing leafref target pointers);
               * if-features are resolved here to make sure that we will have all if-features for
               * later check of feature circular dependency */
              if (unres->type[i] & types) {
                  ++unres_count;
                  rc = resolve_unres_schema_item(unres->module[i], unres->item[i], unres->type[i], unres->str_snode[i], unres);
                  if (unres->type[i] == UNRES_EXT_FINALIZE) {
                      /* to avoid double free */
                      unres->type[i] = UNRES_RESOLVED;
                  }
                  if (!rc || (unres->type[i] == UNRES_XPATH)) {
                      /* invalid XPath can never cause an error, only a warning */
                      if (unres->type[i] == UNRES_LIST_UNIQ) {
                          /* free the allocated structure */
                          free(unres->item[i]);
                      }
  
                      unres->type[i] = UNRES_RESOLVED;
                      ++(*resolved);
                      ++res_count;
                  } else if ((rc == EXIT_FAILURE) && forward_ref) {
                      /* forward reference, erase errors */
                      ly_err_free_next(ctx, prev_eitem);
                  } else if (print_all_errors) {
                      /* just so that we quit the loop */
                      ++res_count;
                      ret = -1;
                  } else {
                      if (forward_ref) {
                          ly_ilo_restore(ctx, prev_ilo, prev_eitem, 1);
                      }
                      return -1;
                  }
              }
          }
      } while (res_count && (res_count < unres_count));
  
      if (res_count < unres_count) {
          assert(forward_ref);
          /* just print the errors (but we must free the ones we have and get them again :-/ ) */
          ly_ilo_restore(ctx, prev_ilo, prev_eitem, 0);
  
          for (i = 0; i < unres->count; ++i) {
              if (unres->type[i] & types) {
                  resolve_unres_schema_item(unres->module[i], unres->item[i], unres->type[i], unres->str_snode[i], unres);
              }
          }
          return -1;
      }
  
      if (forward_ref) {
          /* restore log */
          ly_ilo_restore(ctx, prev_ilo, prev_eitem, 0);
          ly_errno = prev_ly_errno;
      }
  
      return ret;
  }
  
  /**
   * @brief Resolve every unres schema item in the structure. Logs directly.
   *
   * @param[in] mod Main module.
   * @param[in] unres Unres schema structure to use.
   *
   * @return EXIT_SUCCESS on success, -1 on error.
   */
  int
  resolve_unres_schema(struct lys_module *mod, struct unres_schema *unres)
  {
      uint32_t resolved = 0;
  
      assert(unres);
  
      LOGVRB("Resolving \"%s\" unresolved schema nodes and their constraints...", mod->name);
  
      /* UNRES_TYPE_LEAFREF must be resolved (for storing leafref target pointers);
       * if-features are resolved here to make sure that we will have all if-features for
       * later check of feature circular dependency */
      if (resolve_unres_schema_types(unres, UNRES_USES | UNRES_IFFEAT | UNRES_TYPE_DER | UNRES_TYPE_DER_TPDF | UNRES_TYPE_DER_TPDF
                                     | UNRES_AUGMENT | UNRES_CHOICE_DFLT | UNRES_IDENT,
                                     mod->ctx, 1, 0, &resolved)) {
          return -1;
      }
  
      if (resolve_unres_schema_types(unres, UNRES_MOD_IMPLEMENT, mod->ctx, 1, 0, &resolved)) {
          return -1;
      }
  
      /* another batch of resolved items (UNRES_MOD_IMPLEMENT must be set again because it can be added again) */
      if (resolve_unres_schema_types(unres, UNRES_TYPE_IDENTREF | UNRES_FEATURE | UNRES_TYPEDEF_DFLT | UNRES_TYPE_DFLT
                                     | UNRES_TYPE_LEAFREF | UNRES_LIST_KEYS | UNRES_LIST_UNIQ | UNRES_EXT | UNRES_MOD_IMPLEMENT,
                                     mod->ctx, 1, 0, &resolved)) {
          return -1;
      }
  
      /* print xpath warnings and finalize extensions, keep it last to provide the complete schema tree information to the plugin's checkers */
      if (resolve_unres_schema_types(unres, UNRES_XPATH | UNRES_EXT_FINALIZE, mod->ctx, 0, 1, &resolved)) {
          return -1;
      }
  
      LOGVRB("All \"%s\" schema nodes and constraints resolved.", mod->name);
      unres->count = 0;
      return EXIT_SUCCESS;
  }
  
  /**
   * @brief Try to resolve an unres schema item with a string argument. Logs indirectly.
   *
   * @param[in] mod Main module.
   * @param[in] unres Unres schema structure to use.
   * @param[in] item Item to resolve. Type determined by \p type.
   * @param[in] type Type of the unresolved item.
   * @param[in] str String argument.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on storing the item in unres, -1 on error.
   */
  int
  unres_schema_add_str(struct lys_module *mod, struct unres_schema *unres, void *item, enum UNRES_ITEM type,
                       const char *str)
  {
      int rc;
      const char *dictstr;
  
      dictstr = lydict_insert(mod->ctx, str, 0);
      rc = unres_schema_add_node(mod, unres, item, type, (struct lys_node *)dictstr);
  
      if (rc < 0) {
          lydict_remove(mod->ctx, dictstr);
      }
      return rc;
  }
  
  /**
   * @brief Try to resolve an unres schema item with a schema node argument. Logs indirectly.
   *
   * @param[in] mod Main module.
   * @param[in] unres Unres schema structure to use.
   * @param[in] item Item to resolve. Type determined by \p type.
   * @param[in] type Type of the unresolved item. UNRES_TYPE_DER is handled specially!
   * @param[in] snode Schema node argument.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on storing the item in unres, -1 on error.
   */
  int
  unres_schema_add_node(struct lys_module *mod, struct unres_schema *unres, void *item, enum UNRES_ITEM type,
                        struct lys_node *snode)
  {
      int rc;
      uint32_t u;
      enum int_log_opts prev_ilo;
      struct ly_err_item *prev_eitem;
      LY_ERR prev_ly_errno;
      struct lyxml_elem *yin;
      struct lys_type *stype;
      struct ly_ctx *ctx = mod->ctx;
  
      assert(unres && (item || (type == UNRES_MOD_IMPLEMENT)) && ((type != UNRES_LEAFREF) && (type != UNRES_INSTID)
             && (type != UNRES_WHEN) && (type != UNRES_MUST)));
  
      /* check for duplicities in unres */
      for (u = 0; u < unres->count; u++) {
          if (unres->type[u] == type && unres->item[u] == item &&
                  unres->str_snode[u] == snode && unres->module[u] == mod) {
              /* duplication can happen when the node contains multiple statements of the same type to check,
               * this can happen for example when refinement is being applied, so we just postpone the processing
               * and do not duplicate the information */
              return EXIT_FAILURE;
          }
      }
  
      if ((type == UNRES_EXT_FINALIZE) || (type == UNRES_XPATH) || (type == UNRES_MOD_IMPLEMENT)) {
          /* extension finalization is not even tried when adding the item into the inres list,
           * xpath is not tried because it would hide some potential warnings,
           * implementing module must be deferred because some other nodes can be added that will need to be traversed
           * and their targets made implemented */
          rc = EXIT_FAILURE;
      } else {
          prev_ly_errno = ly_errno;
          ly_ilo_change(ctx, ILO_STORE, &prev_ilo, &prev_eitem);
  
          rc = resolve_unres_schema_item(mod, item, type, snode, unres);
          if (rc != EXIT_FAILURE) {
              ly_ilo_restore(ctx, prev_ilo, prev_eitem, rc == -1 ? 1 : 0);
              if (rc != -1) {
                  /* print warnings here so that they are actually printed */
                  if ((type == UNRES_TYPE_DER_TPDF) || (type == UNRES_TYPE_DER)) {
                      stype = item;
                      if (stype->der->module && (((stype->base == LY_TYPE_LEAFREF)
                              && (stype->info.lref.req != stype->der->type.info.lref.req)) || ((stype->base == LY_TYPE_INST)
                              && (stype->info.inst.req != stype->der->type.info.inst.req)))) {
                          if (type == UNRES_TYPE_DER_TPDF) {
                              /* typedef */
                              LOGWRN(ctx, "Derived typedef \"%s\" is changing the \"require-instance\" property, "
                                     "which is discouraged.", stype->parent->name);
                          } else {
                              /* leaf */
                              LOGWRN(ctx, "Node \"%s\" type is changing the \"require-instance\" property, "
                                     "which is discouraged.", snode->name);
                          }
                      }
                  }
                  ly_errno = prev_ly_errno;
              }
  
              if (type == UNRES_LIST_UNIQ) {
                  /* free the allocated structure */
                  free(item);
              } else if (rc == -1 && type == UNRES_IFFEAT) {
                  /* free the allocated resources */
                  free(*((char **)item));
              }
              return rc;
          } else {
              /* erase info about validation errors */
              ly_ilo_restore(ctx, prev_ilo, prev_eitem, 0);
              ly_errno = prev_ly_errno;
          }
  
          print_unres_schema_item_fail(item, type, snode);
  
          /* HACK unlinking is performed here so that we do not do any (NS) copying in vain */
          if (type == UNRES_TYPE_DER || type == UNRES_TYPE_DER_TPDF) {
              yin = (struct lyxml_elem *)((struct lys_type *)item)->der;
              if (!(yin->flags & LY_YANG_STRUCTURE_FLAG)) {
                  lyxml_unlink_elem(mod->ctx, yin, 1);
                  ((struct lys_type *)item)->der = (struct lys_tpdf *)yin;
              }
          }
      }
  
      unres->count++;
      unres->item = ly_realloc(unres->item, unres->count*sizeof *unres->item);
      LY_CHECK_ERR_RETURN(!unres->item, LOGMEM(ctx), -1);
      unres->item[unres->count-1] = item;
      unres->type = ly_realloc(unres->type, unres->count*sizeof *unres->type);
      LY_CHECK_ERR_RETURN(!unres->type, LOGMEM(ctx), -1);
      unres->type[unres->count-1] = type;
      unres->str_snode = ly_realloc(unres->str_snode, unres->count*sizeof *unres->str_snode);
      LY_CHECK_ERR_RETURN(!unres->str_snode, LOGMEM(ctx), -1);
      unres->str_snode[unres->count-1] = snode;
      unres->module = ly_realloc(unres->module, unres->count*sizeof *unres->module);
      LY_CHECK_ERR_RETURN(!unres->module, LOGMEM(ctx), -1);
      unres->module[unres->count-1] = mod;
  
      return rc;
  }
  
  /**
   * @brief Duplicate an unres schema item. Logs indirectly.
   *
   * @param[in] mod Main module.
   * @param[in] unres Unres schema structure to use.
   * @param[in] item Old item to be resolved.
   * @param[in] type Type of the old unresolved item.
   * @param[in] new_item New item to use in the duplicate.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE if item is not in unres, -1 on error.
   */
  int
  unres_schema_dup(struct lys_module *mod, struct unres_schema *unres, void *item, enum UNRES_ITEM type, void *new_item)
  {
      int i;
      struct unres_list_uniq aux_uniq;
      struct unres_iffeat_data *iff_data;
  
      assert(item && new_item && ((type != UNRES_LEAFREF) && (type != UNRES_INSTID) && (type != UNRES_WHEN)));
  
      /* hack for UNRES_LIST_UNIQ, which stores multiple items behind its item */
      if (type == UNRES_LIST_UNIQ) {
          aux_uniq.list = item;
          aux_uniq.expr = ((struct unres_list_uniq *)new_item)->expr;
          item = &aux_uniq;
      }
      i = unres_schema_find(unres, -1, item, type);
  
      if (i == -1) {
          if (type == UNRES_LIST_UNIQ) {
              free(new_item);
          }
          return EXIT_FAILURE;
      }
  
      if ((type == UNRES_TYPE_LEAFREF) || (type == UNRES_USES) || (type == UNRES_TYPE_DFLT) ||
              (type == UNRES_FEATURE) || (type == UNRES_LIST_UNIQ)) {
          if (unres_schema_add_node(mod, unres, new_item, type, unres->str_snode[i]) == -1) {
              LOGINT(mod->ctx);
              return -1;
          }
      } else if (type == UNRES_IFFEAT) {
          /* duplicate unres_iffeature_data */
          iff_data = malloc(sizeof *iff_data);
          LY_CHECK_ERR_RETURN(!iff_data, LOGMEM(mod->ctx), -1);
          iff_data->fname = lydict_insert(mod->ctx, ((struct unres_iffeat_data *)unres->str_snode[i])->fname, 0);
          iff_data->node = ((struct unres_iffeat_data *)unres->str_snode[i])->node;
          iff_data->infeature = ((struct unres_iffeat_data *)unres->str_snode[i])->infeature;
          if (unres_schema_add_node(mod, unres, new_item, type, (struct lys_node *)iff_data) == -1) {
              LOGINT(mod->ctx);
              return -1;
          }
      } else {
          if (unres_schema_add_str(mod, unres, new_item, type, unres->str_snode[i]) == -1) {
              LOGINT(mod->ctx);
              return -1;
          }
      }
  
      return EXIT_SUCCESS;
  }
  
  /* does not log */
  int
  unres_schema_find(struct unres_schema *unres, int start_on_backwards, void *item, enum UNRES_ITEM type)
  {
      int i;
      struct unres_list_uniq *aux_uniq1, *aux_uniq2;
  
      if (!unres->count) {
          return -1;
      }
  
      if (start_on_backwards >= 0) {
          i = start_on_backwards;
      } else {
          i = unres->count - 1;
      }
      for (; i > -1; i--) {
          if (unres->type[i] != type) {
              continue;
          }
          if (type != UNRES_LIST_UNIQ) {
              if (unres->item[i] == item) {
                  break;
              }
          } else {
              aux_uniq1 = (struct unres_list_uniq *)unres->item[i];
              aux_uniq2 = (struct unres_list_uniq *)item;
              if ((aux_uniq1->list == aux_uniq2->list) && ly_strequal(aux_uniq1->expr, aux_uniq2->expr, 0)) {
                  break;
              }
          }
      }
  
      return i;
  }
  
  static void
  unres_schema_free_item(struct ly_ctx *ctx, struct unres_schema *unres, uint32_t i)
  {
      struct lyxml_elem *yin;
      struct yang_type *yang;
      struct unres_iffeat_data *iff_data;
  
      switch (unres->type[i]) {
      case UNRES_TYPE_DER_TPDF:
      case UNRES_TYPE_DER:
          yin = (struct lyxml_elem *)((struct lys_type *)unres->item[i])->der;
          if (yin->flags & LY_YANG_STRUCTURE_FLAG) {
              yang =(struct yang_type *)yin;
              ((struct lys_type *)unres->item[i])->base = yang->base;
              lydict_remove(ctx, yang->name);
              free(yang);
              if (((struct lys_type *)unres->item[i])->base == LY_TYPE_UNION) {
                  yang_free_type_union(ctx, (struct lys_type *)unres->item[i]);
              }
          } else {
              lyxml_free(ctx, yin);
          }
          break;
      case UNRES_IFFEAT:
          iff_data = (struct unres_iffeat_data *)unres->str_snode[i];
          lydict_remove(ctx, iff_data->fname);
          free(unres->str_snode[i]);
          break;
      case UNRES_IDENT:
      case UNRES_TYPE_IDENTREF:
      case UNRES_CHOICE_DFLT:
      case UNRES_LIST_KEYS:
          lydict_remove(ctx, (const char *)unres->str_snode[i]);
          break;
      case UNRES_LIST_UNIQ:
          free(unres->item[i]);
          break;
      case UNRES_EXT:
          free(unres->str_snode[i]);
          break;
      case UNRES_EXT_FINALIZE:
          free(unres->str_snode[i]);
      default:
          break;
      }
      unres->type[i] = UNRES_RESOLVED;
  }
  
  void
  unres_schema_free(struct lys_module *module, struct unres_schema **unres, int all)
  {
      uint32_t i;
      unsigned int unresolved = 0;
  
      if (!unres || !(*unres)) {
          return;
      }
  
      assert(module || ((*unres)->count == 0));
  
      for (i = 0; i < (*unres)->count; ++i) {
          if (!all && ((*unres)->module[i] != module)) {
              if ((*unres)->type[i] != UNRES_RESOLVED) {
                  unresolved++;
              }
              continue;
          }
  
          /* free heap memory for the specific item */
          unres_schema_free_item(module->ctx, *unres, i);
      }
  
      /* free it all */
      if (!module || all || (!unresolved && !module->type)) {
          free((*unres)->item);
          free((*unres)->type);
          free((*unres)->str_snode);
          free((*unres)->module);
          free((*unres));
          (*unres) = NULL;
      }
  }
  
  /* check whether instance-identifier points outside its data subtree (for operation it is any node
   * outside the operation subtree, otherwise it is a node from a foreign model) */
  static int
  check_instid_ext_dep(const struct lys_node *sleaf, const char *json_instid)
  {
      const struct lys_node *op_node, *first_node;
      enum int_log_opts prev_ilo;
      char *buf, *tmp;
  
      if (!json_instid || !json_instid[0]) {
          /* no/empty value */
          return 0;
      }
  
      for (op_node = lys_parent(sleaf);
           op_node && !(op_node->nodetype & (LYS_NOTIF | LYS_RPC | LYS_ACTION));
           op_node = lys_parent(op_node));
  
      if (op_node && lys_parent(op_node)) {
          /* nested operation - any absolute path is external */
          return 1;
      }
  
      /* get the first node from the instid */
      tmp = strchr(json_instid + 1, '/');
      buf = strndup(json_instid, tmp ? (size_t)(tmp - json_instid) : strlen(json_instid));
      if (!buf) {
          /* so that we do not have to bother with logging, say it is not external */
          return 0;
      }
  
      /* find the first schema node, do not log */
      ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
      first_node = ly_ctx_get_node(NULL, sleaf, buf, 0);
      ly_ilo_restore(NULL, prev_ilo, NULL, 0);
  
      free(buf);
      if (!first_node) {
          /* unknown path, say it is external */
          return 1;
      }
  
      /* based on the first schema node in the path we can decide whether it points to an external tree or not */
  
      if (op_node) {
          if (op_node != first_node) {
              /* it is a top-level operation, so we're good if it points somewhere inside it */
              return 1;
          }
      } else {
          if (lys_node_module(sleaf) != lys_node_module(first_node)) {
              /* modules differ */
              return 1;
          }
      }
  
      return 0;
  }
  
  int
  resolve_instid(struct lyd_node *data, const char *path, int req_inst, struct lyd_node **ret)
  {
      int i = 0, j, parsed, cur_idx;
      const struct lys_module *mod, *prev_mod = NULL;
      struct ly_ctx *ctx = data->schema->module->ctx;
      struct lyd_node *root, *node;
      const char *model = NULL, *name;
      char *str;
      int mod_len, name_len, has_predicate;
      struct unres_data node_match;
  
      memset(&node_match, 0, sizeof node_match);
      *ret = NULL;
  
      /* we need root to resolve absolute path */
      for (root = data; root->parent; root = root->parent);
      /* we're still parsing it and the pointer is not correct yet */
      if (root->prev) {
          for (; root->prev->next; root = root->prev);
      }
  
      /* search for the instance node */
      while (path[i]) {
          j = parse_instance_identifier(&path[i], &model, &mod_len, &name, &name_len, &has_predicate);
          if (j <= 0) {
              LOGVAL(ctx, LYE_INCHAR, LY_VLOG_LYD, data, path[i-j], &path[i-j]);
              goto error;
          }
          i += j;
  
          if (model) {
              str = strndup(model, mod_len);
              if (!str) {
                  LOGMEM(ctx);
                  goto error;
              }
              mod = ly_ctx_get_module(ctx, str, NULL, 1);
              if (ctx->data_clb) {
                  if (!mod) {
                      mod = ctx->data_clb(ctx, str, NULL, 0, ctx->data_clb_data);
                  } else if (!mod->implemented) {
                      mod = ctx->data_clb(ctx, mod->name, mod->ns, LY_MODCLB_NOT_IMPLEMENTED, ctx->data_clb_data);
                  }
              }
              free(str);
  
              if (!mod || !mod->implemented || mod->disabled) {
                  break;
              }
          } else if (!prev_mod) {
              /* first iteration and we are missing module name */
              LOGVAL(ctx, LYE_INELEM_LEN, LY_VLOG_LYD, data, name_len, name);
              LOGVAL(ctx, LYE_SPEC, LY_VLOG_PREV, NULL, "Instance-identifier is missing prefix in the first node.");
              goto error;
          } else {
              mod = prev_mod;
          }
  
          if (resolve_data(mod, name, name_len, root, &node_match)) {
              /* no instance exists */
              break;
          }
  
          if (has_predicate) {
              /* we have predicate, so the current results must be list or leaf-list */
              parsed = j = 0;
              /* index of the current node (for lists with position predicates) */
              cur_idx = 1;
              while (j < (signed)node_match.count) {
                  node = node_match.node[j];
                  parsed = resolve_instid_predicate(mod, &path[i], &node, cur_idx);
                  if (parsed < 1) {
                      LOGVAL(ctx, LYE_INPRED, LY_VLOG_LYD, data, &path[i - parsed]);
                      goto error;
                  }
  
                  if (!node) {
                      /* current node does not satisfy the predicate */
                      unres_data_del(&node_match, j);
                  } else {
                      ++j;
                  }
                  ++cur_idx;
              }
  
              i += parsed;
          } else if (node_match.count) {
              /* check that we are not addressing lists */
              for (j = 0; (unsigned)j < node_match.count; ++j) {
                  if (node_match.node[j]->schema->nodetype == LYS_LIST) {
                      unres_data_del(&node_match, j--);
                  }
              }
              if (!node_match.count) {
                  LOGVAL(ctx, LYE_SPEC, LY_VLOG_LYD, data, "Instance identifier is missing list keys.");
              }
          }
  
          prev_mod = mod;
      }
  
      if (!node_match.count) {
          /* no instance exists */
          if (req_inst > -1) {
              LOGVAL(ctx, LYE_NOREQINS, LY_VLOG_LYD, data, path);
              return EXIT_FAILURE;
          }
          LOGVRB("There is no instance of \"%s\", but it is not required.", path);
          return EXIT_SUCCESS;
      } else if (node_match.count > 1) {
          /* instance identifier must resolve to a single node */
          LOGVAL(ctx, LYE_TOOMANY, LY_VLOG_LYD, data, path, "data tree");
          goto error;
      } else {
          /* we have required result, remember it and cleanup */
          *ret = node_match.node[0];
          free(node_match.node);
          return EXIT_SUCCESS;
      }
  
  error:
      /* cleanup */
      free(node_match.node);
      return -1;
  }
  
  int
  resolve_leafref(struct lyd_node_leaf_list *leaf, const char *path, int req_inst, struct lyd_node **ret)
  {
      struct lyxp_set xp_set;
      uint32_t i;
  
      memset(&xp_set, 0, sizeof xp_set);
      *ret = NULL;
  
      /* syntax was already checked, so just evaluate the path using standard XPath */
      if (lyxp_eval(path, (struct lyd_node *)leaf, LYXP_NODE_ELEM, lyd_node_module((struct lyd_node *)leaf), &xp_set, 0) != EXIT_SUCCESS) {
          return -1;
      }
  
      if (xp_set.type == LYXP_SET_NODE_SET) {
          for (i = 0; i < xp_set.used; ++i) {
              if ((xp_set.val.nodes[i].type != LYXP_NODE_ELEM) || !(xp_set.val.nodes[i].node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
                  continue;
              }
  
              /* not that the value is already in canonical form since the parsers does the conversion,
               * so we can simply compare just the values */
              if (ly_strequal(leaf->value_str, ((struct lyd_node_leaf_list *)xp_set.val.nodes[i].node)->value_str, 1)) {
                  /* we have the match */
                  *ret = xp_set.val.nodes[i].node;
                  break;
              }
          }
      }
  
      lyxp_set_cast(&xp_set, LYXP_SET_EMPTY, (struct lyd_node *)leaf, NULL, 0);
  
      if (!*ret) {
          /* reference not found */
          if (req_inst > -1) {
              LOGVAL(leaf->schema->module->ctx, LYE_NOLEAFREF, LY_VLOG_LYD, leaf, path, leaf->value_str);
              return EXIT_FAILURE;
          } else {
              LOGVRB("There is no leafref \"%s\" with the value \"%s\", but it is not required.", path, leaf->value_str);
          }
      }
  
      return EXIT_SUCCESS;
  }
  
  /* ignore fail because we are parsing edit-config, get, or get-config - but only if the union includes leafref or instid */
  int
  resolve_union(struct lyd_node_leaf_list *leaf, struct lys_type *type, int store, int ignore_fail,
                struct lys_type **resolved_type)
  {
      struct ly_ctx *ctx = leaf->schema->module->ctx;
      struct lys_type *t;
      struct lyd_node *ret;
      enum int_log_opts prev_ilo;
      int found, success = 0, ext_dep, req_inst;
      const char *json_val = NULL;
  
      assert(type->base == LY_TYPE_UNION);
  
      if ((leaf->value_type == LY_TYPE_UNION) || ((leaf->value_type == LY_TYPE_INST) && (leaf->value_flags & LY_VALUE_UNRES))) {
          /* either NULL or instid previously converted to JSON */
          json_val = lydict_insert(ctx, leaf->value.string, 0);
      }
  
      if (store) {
          lyd_free_value(leaf->value, leaf->value_type, leaf->value_flags, &((struct lys_node_leaf *)leaf->schema)->type,
                         leaf->value_str, NULL, NULL, NULL);
          memset(&leaf->value, 0, sizeof leaf->value);
      }
  
      /* turn logging off, we are going to try to validate the value with all the types in order */
      ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, 0);
  
      t = NULL;
      found = 0;
      while ((t = lyp_get_next_union_type(type, t, &found))) {
          found = 0;
  
          switch (t->base) {
          case LY_TYPE_LEAFREF:
              if ((ignore_fail == 1) || ((leaf->schema->flags & LYS_LEAFREF_DEP) && (ignore_fail == 2))) {
                  req_inst = -1;
              } else {
                  req_inst = t->info.lref.req;
              }
  
              if (!resolve_leafref(leaf, t->info.lref.path, req_inst, &ret)) {
                  if (store) {
                      if (ret && !(leaf->schema->flags & LYS_LEAFREF_DEP)) {
                          /* valid resolved */
                          leaf->value.leafref = ret;
                          leaf->value_type = LY_TYPE_LEAFREF;
                      } else {
                          /* valid unresolved */
                          ly_ilo_restore(NULL, prev_ilo, NULL, 0);
                          if (!lyp_parse_value(t, &leaf->value_str, NULL, leaf, NULL, NULL, 1, 0)) {
                              return -1;
                          }
                          ly_ilo_change(NULL, ILO_IGNORE, &prev_ilo, NULL);
                      }
                  }
  
                  success = 1;
              }
              break;
          case LY_TYPE_INST:
              ext_dep = check_instid_ext_dep(leaf->schema, (json_val ? json_val : leaf->value_str));
              if ((ignore_fail == 1) || (ext_dep && (ignore_fail == 2))) {
                  req_inst = -1;
              } else {
                  req_inst = t->info.inst.req;
              }
  
              if (!resolve_instid((struct lyd_node *)leaf, (json_val ? json_val : leaf->value_str), req_inst, &ret)) {
                  if (store) {
                      if (ret && !ext_dep) {
                          /* valid resolved */
                          leaf->value.instance = ret;
                          leaf->value_type = LY_TYPE_INST;
  
                          if (json_val) {
                              lydict_remove(leaf->schema->module->ctx, leaf->value_str);
                              leaf->value_str = json_val;
                              json_val = NULL;
                          }
                      } else {
                          /* valid unresolved */
                          if (json_val) {
                              /* put the JSON val back */
                              leaf->value.string = json_val;
                              json_val = NULL;
                          } else {
                              leaf->value.instance = NULL;
                          }
                          leaf->value_type = LY_TYPE_INST;
                          leaf->value_flags |= LY_VALUE_UNRES;
                      }
                  }
  
                  success = 1;
              }
              break;
          default:
              if (lyp_parse_value(t, &leaf->value_str, NULL, leaf, NULL, NULL, store, 0)) {
                  success = 1;
              }
              break;
          }
  
          if (success) {
              break;
          }
  
          /* erase possible present and invalid value data */
          if (store) {
              lyd_free_value(leaf->value, leaf->value_type, leaf->value_flags, t, leaf->value_str, NULL, NULL, NULL);
              memset(&leaf->value, 0, sizeof leaf->value);
          }
      }
  
      /* turn logging back on */
      ly_ilo_restore(NULL, prev_ilo, NULL, 0);
  
      if (json_val) {
          if (!success) {
              /* put the value back for now */
              assert(leaf->value_type == LY_TYPE_UNION);
              leaf->value.string = json_val;
          } else {
              /* value was ultimately useless, but we could not have known */
              lydict_remove(leaf->schema->module->ctx, json_val);
          }
      }
  
      if (success) {
          if (resolved_type) {
              *resolved_type = t;
          }
      } else if (!ignore_fail || !type->info.uni.has_ptr_type) {
          /* not found and it is required */
          LOGVAL(ctx, LYE_INVAL, LY_VLOG_LYD, leaf, leaf->value_str ? leaf->value_str : "", leaf->schema->name);
          return EXIT_FAILURE;
      }
  
      return EXIT_SUCCESS;
  
  }
  
  /**
   * @brief Resolve a single unres data item. Logs directly.
   *
   * @param[in] node Data node to resolve.
   * @param[in] type Type of the unresolved item.
   * @param[in] ignore_fail 0 - no, 1 - yes, 2 - yes, but only for external dependencies.
   *
   * @return EXIT_SUCCESS on success, EXIT_FAILURE on forward reference, -1 on error.
   */
  int
  resolve_unres_data_item(struct lyd_node *node, enum UNRES_ITEM type, int ignore_fail, struct lys_when **failed_when)
  {
      int rc, req_inst, ext_dep;
      struct lyd_node_leaf_list *leaf;
      struct lyd_node *ret;
      struct lys_node_leaf *sleaf;
  
      leaf = (struct lyd_node_leaf_list *)node;
      sleaf = (struct lys_node_leaf *)leaf->schema;
  
      switch (type) {
      case UNRES_LEAFREF:
          assert(sleaf->type.base == LY_TYPE_LEAFREF);
          if (ignore_fail) {
              req_inst = -1;
          } else {
              req_inst = sleaf->type.info.lref.req;
          }
          if ((leaf->schema->flags & LYS_LEAFREF_DEP) && (ignore_fail == 2)) {
              /* do not even try to resolve */
              rc = 0;
              ret = NULL;
          } else {
              rc = resolve_leafref(leaf, sleaf->type.info.lref.path, req_inst, &ret);
          }
          if (!rc) {
              if (ret && !(leaf->schema->flags & LYS_LEAFREF_DEP)) {
                  /* valid resolved */
                  if (leaf->value_type == LY_TYPE_BITS) {
                      free(leaf->value.bit);
                  }
                  leaf->value.leafref = ret;
                  leaf->value_type = LY_TYPE_LEAFREF;
                  leaf->value_flags &= ~LY_VALUE_UNRES;
              } else {
                  /* valid unresolved */
                  if (!(leaf->value_flags & LY_VALUE_UNRES)) {
                      if (!lyp_parse_value(&sleaf->type, &leaf->value_str, NULL, leaf, NULL, NULL, 1, 0)) {
                          return -1;
                      }
                  }
              }
          } else {
              return rc;
          }
          break;
  
      case UNRES_INSTID:
          assert(sleaf->type.base == LY_TYPE_INST);
          ext_dep = check_instid_ext_dep(leaf->schema, leaf->value_str);
          if (ext_dep == -1) {
              return -1;
          }
  
          if (ignore_fail) {
              req_inst = -1;
          } else {
              req_inst = sleaf->type.info.inst.req;
          }
          if (ext_dep && (ignore_fail == 2)) {
              /* do not even try to resolve */
              rc = 0;
              ret = NULL;
          } else {
              rc = resolve_instid(node, leaf->value_str, req_inst, &ret);
          }
          if (!rc) {
              if (ret && !ext_dep) {
                  /* valid resolved */
                  leaf->value.instance = ret;
                  leaf->value_type = LY_TYPE_INST;
                  leaf->value_flags &= ~LY_VALUE_UNRES;
              } else {
                  /* valid unresolved */
                  leaf->value.instance = NULL;
                  leaf->value_type = LY_TYPE_INST;
                  leaf->value_flags |= LY_VALUE_UNRES;
              }
          } else {
              return rc;
          }
          break;
  
      case UNRES_UNION:
          assert(sleaf->type.base == LY_TYPE_UNION);
          return resolve_union(leaf, &sleaf->type, 1, ignore_fail, NULL);
  
      case UNRES_WHEN:
          if ((rc = resolve_when(node, ignore_fail, failed_when))) {
              return rc;
          }
          break;
  
      case UNRES_MUST:
          if ((rc = resolve_must(node, 0, ignore_fail))) {
              return rc;
          }
          break;
  
      case UNRES_MUST_INOUT:
          if ((rc = resolve_must(node, 1, ignore_fail))) {
              return rc;
          }
          break;
  
      case UNRES_UNIQ_LEAVES:
          if (lyv_data_unique(node)) {
              return -1;
          }
          break;
  
      default:
          LOGINT(NULL);
          return -1;
      }
  
      return EXIT_SUCCESS;
  }
  
  /**
   * @brief add data unres item
   *
   * @param[in] unres Unres data structure to use.
   * @param[in] node Data node to use.
   *
   * @return 0 on success, -1 on error.
   */
  int
  unres_data_add(struct unres_data *unres, struct lyd_node *node, enum UNRES_ITEM type)
  {
      assert(unres && node);
      assert((type == UNRES_LEAFREF) || (type == UNRES_INSTID) || (type == UNRES_WHEN) || (type == UNRES_MUST)
             || (type == UNRES_MUST_INOUT) || (type == UNRES_UNION) || (type == UNRES_UNIQ_LEAVES));
  
      unres->count++;
      unres->node = ly_realloc(unres->node, unres->count * sizeof *unres->node);
      LY_CHECK_ERR_RETURN(!unres->node, LOGMEM(NULL), -1);
      unres->node[unres->count - 1] = node;
      unres->type = ly_realloc(unres->type, unres->count * sizeof *unres->type);
      LY_CHECK_ERR_RETURN(!unres->type, LOGMEM(NULL), -1);
      unres->type[unres->count - 1] = type;
  
      return 0;
  }
  
  static void
  resolve_unres_data_autodel_diff(struct unres_data *unres, uint32_t unres_i)
  {
      struct lyd_node *next, *child, *parent;
      uint32_t i;
  
      for (i = 0; i < unres->diff_idx; ++i) {
          if (unres->diff->type[i] == LYD_DIFF_DELETED) {
              /* only leaf(-list) default could be removed and there is nothing to be checked in that case */
              continue;
          }
  
          if (unres->diff->second[i] == unres->node[unres_i]) {
              /* 1) default value was supposed to be created, but is disabled by when
               * -> remove it from diff altogether
               */
              unres_data_diff_rem(unres, i);
              /* if diff type is CREATED, the value was just a pointer, it can be freed normally (unlike in 4) */
              return;
          } else {
              parent = unres->diff->second[i]->parent;
              while (parent && (parent != unres->node[unres_i])) {
                  parent = parent->parent;
              }
              if (parent) {
                  /* 2) default value was supposed to be created but is disabled by when in some parent
                   * -> remove this default subtree and add the rest into diff as deleted instead in 4)
                   */
                  unres_data_diff_rem(unres, i);
                  break;
              }
  
              LY_TREE_DFS_BEGIN(unres->diff->second[i], next, child) {
                  if (child == unres->node[unres_i]) {
                      /* 3) some default child of a default value was supposed to be created but has false when
                       * -> the subtree will be freed later and automatically disconnected from the diff parent node
                       */
                      return;
                  }
  
                  LY_TREE_DFS_END(unres->diff->second[i], next, child);
              }
          }
      }
  
      /* 4) it does not overlap with created default values in any way
       * -> just add it into diff as deleted
       */
      unres_data_diff_new(unres, unres->node[unres_i], unres->node[unres_i]->parent, 0);
      lyd_unlink(unres->node[unres_i]);
  
      /* should not be freed anymore */
      unres->node[unres_i] = NULL;
  }
  
  /**
   * @brief Resolve every unres data item in the structure. Logs directly.
   *
   * If options include #LYD_OPT_TRUSTED, the data are considered trusted (must conditions are not expected,
   * unresolved leafrefs/instids are accepted, when conditions are normally resolved because at least some implicit
   * non-presence containers may need to be deleted).
   *
   * If options includes #LYD_OPT_WHENAUTODEL, the non-default nodes with false when conditions are auto-deleted.
   *
   * @param[in] ctx Context used.
   * @param[in] unres Unres data structure to use.
   * @param[in,out] root Root node of the data tree, can be changed due to autodeletion.
   * @param[in] options Data options as described above.
   *
   * @return EXIT_SUCCESS on success, -1 on error.
   */
  int
  resolve_unres_data(struct ly_ctx *ctx, struct unres_data *unres, struct lyd_node **root, int options)
  {
      uint32_t i, j, first, resolved, del_items, stmt_count;
      uint8_t prev_when_status;
      int rc, progress, ignore_fail;
      enum int_log_opts prev_ilo;
      struct ly_err_item *prev_eitem;
      LY_ERR prev_ly_errno = ly_errno;
      struct lyd_node *parent;
      struct lys_when *when;
  
      assert(root);
      assert(unres);
  
      if (!unres->count) {
          return EXIT_SUCCESS;
      }
  
      if (options & (LYD_OPT_NOTIF_FILTER | LYD_OPT_GET | LYD_OPT_GETCONFIG | LYD_OPT_EDIT)) {
          ignore_fail = 1;
      } else if (options & LYD_OPT_NOEXTDEPS) {
          ignore_fail = 2;
      } else {
          ignore_fail = 0;
      }
  
      LOGVRB("Resolving unresolved data nodes and their constraints...");
      if (!ignore_fail) {
          /* remember logging state only if errors are generated and valid */
          ly_ilo_change(ctx, ILO_STORE, &prev_ilo, &prev_eitem);
      }
  
      /*
       * when-stmt first
       */
      first = 1;
      stmt_count = 0;
      resolved = 0;
      del_items = 0;
      do {
          if (!ignore_fail) {
              ly_err_free_next(ctx, prev_eitem);
          }
          progress = 0;
          for (i = 0; i < unres->count; i++) {
              if (unres->type[i] != UNRES_WHEN) {
                  continue;
              }
              if (first) {
                  /* count when-stmt nodes in unres list */
                  stmt_count++;
              }
  
              /* resolve when condition only when all parent when conditions are already resolved */
              for (parent = unres->node[i]->parent;
                   parent && LYD_WHEN_DONE(parent->when_status);
                   parent = parent->parent) {
                  if (!parent->parent && (parent->when_status & LYD_WHEN_FALSE)) {
                      /* the parent node was already unlinked, do not resolve this node,
                       * it will be removed anyway, so just mark it as resolved
                       */
                      unres->node[i]->when_status |= LYD_WHEN_FALSE;
                      unres->type[i] = UNRES_RESOLVED;
                      resolved++;
                      break;
                  }
              }
              if (parent) {
                  continue;
              }
  
              prev_when_status = unres->node[i]->when_status;
              rc = resolve_unres_data_item(unres->node[i], unres->type[i], ignore_fail, &when);
              if (!rc) {
                  /* finish with error/delete the node only if when was changed from true to false, an external
                   * dependency was not required, or it was not provided (the flag would not be passed down otherwise,
                   * checked in upper functions) */
                  if ((unres->node[i]->when_status & LYD_WHEN_FALSE)
                          && (!(when->flags & (LYS_XPCONF_DEP | LYS_XPSTATE_DEP)) || !(options & LYD_OPT_NOEXTDEPS))) {
                      if ((!(prev_when_status & LYD_WHEN_TRUE) || !(options & LYD_OPT_WHENAUTODEL)) && !unres->node[i]->dflt) {
                          /* false when condition */
                          goto error;
                      } /* follows else */
  
                      /* auto-delete */
                      LOGVRB("Auto-deleting node \"%s\" due to when condition (%s)", ly_errpath(ctx), when->cond);
  
                      /* do not delete yet, the subtree can contain another nodes stored in the unres list */
                      /* if it has parent non-presence containers that would be empty, we should actually
                       * remove the container
                       */
                      for (parent = unres->node[i];
                              parent->parent && parent->parent->schema->nodetype == LYS_CONTAINER;
                              parent = parent->parent) {
                          if (((struct lys_node_container *)parent->parent->schema)->presence) {
                              /* presence container */
                              break;
                          }
                          if (parent->next || parent->prev != parent) {
                              /* non empty (the child we are in and we are going to remove is not the only child) */
                              break;
                          }
                      }
                      unres->node[i] = parent;
  
                      if (*root && *root == unres->node[i]) {
                          *root = (*root)->next;
                      }
  
                      unres->type[i] = UNRES_DELETE;
                      del_items++;
  
                      /* update the rest of unres items */
                      for (j = 0; j < unres->count; j++) {
                          if (unres->type[j] == UNRES_RESOLVED || unres->type[j] == UNRES_DELETE) {
                              continue;
                          }
  
                          /* test if the node is in subtree to be deleted */
                          for (parent = unres->node[j]; parent; parent = parent->parent) {
                              if (parent == unres->node[i]) {
                                  /* yes, it is */
                                  unres->type[j] = UNRES_RESOLVED;
                                  resolved++;
                                  break;
                              }
                          }
                      }
                  } else {
                      unres->type[i] = UNRES_RESOLVED;
                  }
                  if (!ignore_fail) {
                      ly_err_free_next(ctx, prev_eitem);
                  }
                  resolved++;
                  progress = 1;
              } else if (rc == -1) {
                  goto error;
              } /* else forward reference */
          }
          first = 0;
      } while (progress && resolved < stmt_count);
  
      /* do we have some unresolved when-stmt? */
      if (stmt_count > resolved) {
          goto error;
      }
  
      for (i = 0; del_items && i < unres->count; i++) {
          /* we had some when-stmt resulted to false, so now we have to sanitize the unres list */
          if (unres->type[i] != UNRES_DELETE) {
              continue;
          }
          if (!unres->node[i]) {
              unres->type[i] = UNRES_RESOLVED;
              del_items--;
              continue;
          }
  
          if (unres->store_diff) {
              resolve_unres_data_autodel_diff(unres, i);
          }
  
          /* really remove the complete subtree */
          lyd_free(unres->node[i]);
          unres->type[i] = UNRES_RESOLVED;
          del_items--;
      }
  
      /*
       * now leafrefs
       */
      if (options & LYD_OPT_TRUSTED) {
          /* we want to attempt to resolve leafrefs */
          assert(!ignore_fail);
          ignore_fail = 1;
  
          ly_ilo_restore(ctx, prev_ilo, prev_eitem, 0);
          ly_errno = prev_ly_errno;
      }
      first = 1;
      stmt_count = 0;
      resolved = 0;
      do {
          progress = 0;
          for (i = 0; i < unres->count; i++) {
              if (unres->type[i] != UNRES_LEAFREF) {
                  continue;
              }
              if (first) {
                  /* count leafref nodes in unres list */
                  stmt_count++;
              }
  
              rc = resolve_unres_data_item(unres->node[i], unres->type[i], ignore_fail, NULL);
              if (!rc) {
                  unres->type[i] = UNRES_RESOLVED;
                  if (!ignore_fail) {
                      ly_err_free_next(ctx, prev_eitem);
                  }
                  resolved++;
                  progress = 1;
              } else if (rc == -1) {
                  goto error;
              } /* else forward reference */
          }
          first = 0;
      } while (progress && resolved < stmt_count);
  
      /* do we have some unresolved leafrefs? */
      if (stmt_count > resolved) {
          goto error;
      }
  
      if (!ignore_fail) {
          /* log normally now, throw away irrelevant errors */
          ly_ilo_restore(ctx, prev_ilo, prev_eitem, 0);
          ly_errno = prev_ly_errno;
      }
  
      /*
       * rest
       */
      for (i = 0; i < unres->count; ++i) {
          if (unres->type[i] == UNRES_RESOLVED) {
              continue;
          }
          assert(!(options & LYD_OPT_TRUSTED) || ((unres->type[i] != UNRES_MUST) && (unres->type[i] != UNRES_MUST_INOUT)));
  
          rc = resolve_unres_data_item(unres->node[i], unres->type[i], ignore_fail, NULL);
          if (rc) {
              /* since when was already resolved, a forward reference is an error */
              return -1;
          }
  
          unres->type[i] = UNRES_RESOLVED;
      }
  
      LOGVRB("All data nodes and constraints resolved.");
      unres->count = 0;
      return EXIT_SUCCESS;
  
  error:
      if (!ignore_fail) {
          /* print all the new errors */
          ly_ilo_restore(ctx, prev_ilo, prev_eitem, 1);
          /* do not restore ly_errno, it was udpated properly */
      }
      return -1;
  }
  