text: /**
   * @file log.c
   * @author Radek Krejci <rkrejci@cesnet.cz>
   * @brief libyang logger implementation
   *
   * Copyright (c) 2015 CESNET, z.s.p.o.
   *
   * This source code is licensed under BSD 3-Clause License (the "License").
   * You may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     https://opensource.org/licenses/BSD-3-Clause
   */
  
  #define _GNU_SOURCE
  #define _BSD_SOURCE
  #define _DEFAULT_SOURCE
  #include <stdarg.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <limits.h>
  #include <string.h>
  
  #include "common.h"
  #include "parser.h"
  #include "context.h"
  #include "tree_internal.h"
  
  volatile int8_t ly_log_level = LY_LLERR;
  static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
  static volatile int path_flag = 1;
  #ifndef NDEBUG
  volatile int ly_log_dbg_groups = 0;
  #endif
  
  API LY_LOG_LEVEL
  ly_verb(LY_LOG_LEVEL level)
  {
      LY_LOG_LEVEL prev = ly_log_level;
  
      ly_log_level = level;
      return prev;
  }
  
  API void
  ly_verb_dbg(int dbg_groups)
  {
  #ifndef NDEBUG
      ly_log_dbg_groups = dbg_groups;
  #else
      (void)dbg_groups;
  #endif
  }
  
  API void
  ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path)
  {
      ly_log_clb = clb;
      path_flag = path;
  }
  
  API void
  (*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *)
  {
      return ly_log_clb;
  }
  
  static void
  log_vprintf(LY_LOG_LEVEL level, uint8_t hide, const char *format, const char *path, va_list args)
  {
      char *msg, *bufdup = NULL;
      struct ly_err_item *eitem;
  
      if ((hide == 0xff) && (level == LY_LLERR)) {
          if (LY_LLWRN > ly_log_level) {
              /* nothing will be printed */
              return;
          } else {
              /* change error to warning */
              level = LY_LLWRN;
          }
      }
  
      if (!format) {
          /* postponed print of path related to the previous error, do not rewrite stored original message */
          msg = "Path is related to the previous error message.";
      } else {
          if (level == LY_LLERR) {
              /* store error message into msg buffer ... */
              msg = ly_err_main.msg;
          } else if (!hide || (hide == 0xff)) {
              /* other messages are stored in working string buffer and not available for later access */
              msg = ly_err_main.buf;
              if (ly_buf_used && msg[0]) {
                  bufdup = strndup(msg, LY_BUF_SIZE - 1);
              }
          } else { /* hide */
              return;
          }
          if (msg != format) {
              vsnprintf(msg, LY_BUF_SIZE - 1, format, args);
              msg[LY_BUF_SIZE - 1] = '\0';
          }
      }
  
      if (level == LY_LLERR) {
          if (!path) {
              /* erase previous path */
              ly_err_main.path_index = LY_BUF_SIZE - 1;
          }
  
          /* if the error-app-tag should be set, do it after calling LOGVAL */
          ly_err_main.apptag[0] = '\0';
  
          /* store error information into a list */
          if (ly_parser_data.ctx) {
              eitem = pthread_getspecific(ly_parser_data.ctx->errlist_key);
              if (!eitem) {
                  eitem = malloc(sizeof *eitem);
                  pthread_setspecific(ly_parser_data.ctx->errlist_key, eitem);
              } else {
                  for (; eitem->next; eitem = eitem->next);
                  eitem->next = malloc(sizeof *eitem->next);
                  eitem = eitem->next;
              }
              if (eitem) {
                  eitem->no = ly_errno;
                  eitem->code = ly_vecode;
                  eitem->msg = strdup(msg);
                  if (path) {
                      eitem->path = strdup(path);
                  } else {
                      eitem->path = NULL;
                  }
                  eitem->next = NULL;
              }
          }
      }
  
      if ((hide && (hide != 0xff)) || (level > ly_log_level)) {
          /* do not print the message */
          goto clean;
      }
  
      if (ly_log_clb) {
          ly_log_clb(level, msg, path);
      } else {
          fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
          if (path) {
              fprintf(stderr, "(path: %s)\n", path);
          }
      }
  
  clean:
      if (bufdup) {
          /* return previous internal buffer content */
          strncpy(msg, bufdup, LY_BUF_SIZE - 1);
          free(bufdup);
      }
  }
  
  void
  ly_log(LY_LOG_LEVEL level, const char *format, ...)
  {
      va_list ap;
  
      va_start(ap, format);
      log_vprintf(level, ly_err_main.vlog_hide, format, NULL, ap);
      va_end(ap);
  }
  
  #ifndef NDEBUG
  
  void
  ly_log_dbg(LY_LOG_DBG_GROUP group, const char *format, ...)
  {
      char *dbg_format;
      const char *str_group;
      va_list ap;
  
      if (!(ly_log_dbg_groups & group)) {
          return;
      }
  
      switch (group) {
      case LY_LDGDICT:
          str_group = "DICT";
          break;
      case LY_LDGYANG:
          str_group = "YANG";
          break;
      case LY_LDGYIN:
          str_group = "YIN";
          break;
      case LY_LDGXPATH:
          str_group = "XPATH";
          break;
      case LY_LDGDIFF:
          str_group = "DIFF";
          break;
      default:
          LOGINT;
          return;
      }
  
      if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
          LOGMEM;
          return;
      }
  
      va_start(ap, format);
      log_vprintf(LY_LLDBG, ly_err_main.vlog_hide, dbg_format, NULL, ap);
      va_end(ap);
  }
  
  #endif
  
  void
  lyext_log(LY_LOG_LEVEL level, const char *plugin, const char *function, const char *format, ...)
  {
      va_list ap;
      char *plugin_msg;
  
      if (level == LY_LLERR) {
          /* set errno */
          ly_errno = LY_EEXT;
      }
      if (ly_log_level < level) {
          return;
      }
  
      if (asprintf(&plugin_msg, "%s (reported by extension plugin %s, %s())", format, plugin, function) == -1) {
          LOGMEM;
          return;
      }
  
      va_start(ap, format);
      log_vprintf(level, ly_err_main.vlog_hide, plugin_msg, NULL, ap);
      va_end(ap);
  
      free(plugin_msg);
  }
  
  const char *ly_errs[] = {
  /* LYE_SUCCESS */      "",
  /* LYE_XML_MISS */     "Missing %s \"%s\".",
  /* LYE_XML_INVAL */    "Invalid %s.",
  /* LYE_XML_INCHAR */   "Encountered invalid character sequence \"%.10s\".",
  
  /* LYE_EOF */          "Unexpected end of input data.",
  /* LYE_INSTMT */       "Invalid keyword \"%s\".",
  /* LYE_INCHILDSTMT */  "Invalid keyword \"%s\" as a child to \"%s\".",
  /* LYE_INPAR */        "Invalid ancestor \"%s\" of \"%s\".",
  /* LYE_INID */         "Invalid identifier \"%s\" (%s).",
  /* LYE_INDATE */       "Invalid date format of \"%s\", \"YYYY-MM-DD\" expected.",
  /* LYE_INARG */        "Invalid value \"%s\" of \"%s\".",
  /* LYE_MISSSTMT */     "Missing keyword \"%s\".",
  /* LYE_MISSCHILDSTMT */ "Missing keyword \"%s\" as a child to \"%s\".",
  /* LYE_MISSARG */      "Missing argument \"%s\" to keyword \"%s\".",
  /* LYE_TOOMANY */      "Too many instances of \"%s\" in \"%s\".",
  /* LYE_DUPID */        "Duplicated %s identifier \"%s\".",
  /* LYE_DUPLEAFLIST */  "Duplicated instance of \"%s\" leaf-list (\"%s\").",
  /* LYE_DUPLIST */      "Duplicated instance of \"%s\" list.",
  /* LYE_NOUNIQ */       "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\".",
  /* LYE_ENUM_INVAL */   "Invalid value \"%d\" of \"%s\" enum, restricted enum value does not match the base type value \"%d\".",
  /* LYE_ENUM_INNAME */  "Adding new enum name \"%s\" in restricted enumeration type is not allowed.",
  /* LYE_ENUM_DUPVAL */  "The value \"%d\" of \"%s\" enum has already been assigned to \"%s\" enum.",
  /* LYE_ENUM_DUPNAME */ "The enum name \"%s\" has already been assigned to another enum.",
  /* LYE_ENUM_WS */      "The enum name \"%s\" includes invalid leading or trailing whitespaces.",
  /* LYE_BITS_INVAL */   "Invalid position \"%d\" of \"%s\" bit, restricted bits position does not match the base type position \"%d\".",
  /* LYE_BITS_INNAME */  "Adding new bit name \"%s\" in restricted bits type is not allowed.",
  /* LYE_BITS_DUPVAL */  "The position \"%d\" of \"%s\" bit has already been assigned to \"%s\" bit.",
  /* LYE_BITS_DUPNAME */ "The bit name \"%s\" has already been assigned to another bit.",
  /* LYE_INMOD */        "Module name \"%s\" refers to an unknown module.",
  /* LYE_INMOD_LEN */    "Module name \"%.*s\" refers to an unknown module.",
  /* LYE_KEY_NLEAF */    "Key \"%s\" is not a leaf.",
  /* LYE_KEY_TYPE */     "Key \"%s\" must not be the built-in type \"empty\".",
  /* LYE_KEY_CONFIG */   "The \"config\" value of the \"%s\" key differs from its list config value.",
  /* LYE_KEY_MISS */     "Leaf \"%s\" defined as key in a list not found.",
  /* LYE_KEY_DUP */      "Key identifier \"%s\" is not unique.",
  /* LYE_INREGEX */      "Regular expression \"%s\" is not valid (\"%s\": %s).",
  /* LYE_INRESOLV */     "Failed to resolve %s \"%s\".",
  /* LYE_INSTATUS */     "A %s definition \"%s\" %s %s definition \"%s\".",
  /* LYE_CIRC_LEAFREFS */"A circular chain of leafrefs detected.",
  /* LYE_CIRC_FEATURES */"A circular chain features detected in \"%s\" feature.",
  /* LYE_CIRC_IMPORTS */ "A circular dependency (import) for module \"%s\".",
  /* LYE_CIRC_INCLUDES */"A circular dependency (include) for submodule \"%s\".",
  /* LYE_INVER */        "Different YANG versions of a submodule and its main module.",
  /* LYE_SUBMODULE */    "Unable to parse submodule, parse the main module instead.",
  
  /* LYE_OBSDATA */      "Obsolete data \"%s\" instantiated.",
  /* LYE_OBSTYPE */      "Data node \"%s\" with obsolete type \"%s\" instantiated.",
  /* LYE_NORESOLV */     "No resolvents found for %s \"%s\".",
  /* LYE_INELEM */       "Unknown element \"%s\".",
  /* LYE_INELEM_LEN */   "Unknown element \"%.*s\".",
  /* LYE_MISSELEM */     "Missing required element \"%s\" in \"%s\".",
  /* LYE_INVAL */        "Invalid value \"%s\" in \"%s\" element.",
  /* LYE_INMETA */       "Invalid \"%s:%s\" metadata with value \"%s\".",
  /* LYE_INATTR */       "Invalid attribute \"%s\".",
  /* LYE_MISSATTR */     "Missing attribute \"%s\" in \"%s\" element.",
  /* LYE_NOCONSTR */     "Value \"%s\" does not satisfy the constraint \"%s\" (range, length, or pattern).",
  /* LYE_INCHAR */       "Unexpected character(s) '%c' (%.15s).",
  /* LYE_INPRED */       "Predicate resolution failed on \"%s\".",
  /* LYE_MCASEDATA */    "Data for more than one case branch of \"%s\" choice present.",
  /* LYE_NOMUST */       "Must condition \"%s\" not satisfied.",
  /* LYE_NOWHEN */       "When condition \"%s\" not satisfied.",
  /* LYE_INORDER */      "Invalid order of elements \"%s\" and \"%s\".",
  /* LYE_INWHEN */       "Irresolvable when condition \"%s\".",
  /* LYE_NOMIN */        "Too few \"%s\" elements.",
  /* LYE_NOMAX */        "Too many \"%s\" elements.",
  /* LYE_NOREQINS */     "Required instance of \"%s\" does not exists.",
  /* LYE_NOLEAFREF */    "Leafref \"%s\" of value \"%s\" points to a non-existing leaf.",
  /* LYE_NOMANDCHOICE */ "Mandatory choice \"%s\" missing a case branch.",
  
  /* LYE_XPATH_INTOK */  "Unexpected XPath token %s (%.15s).",
  /* LYE_XPATH_EOF */    "Unexpected XPath expression end.",
  /* LYE_XPATH_INOP_1 */ "Cannot apply XPath operation %s on %s.",
  /* LYE_XPATH_INOP_2 */ "Cannot apply XPath operation %s on %s and %s.",
  /* LYE_XPATH_INCTX */  "Invalid context type %s in %s.",
  /* LYE_XPATH_INMOD */  "Unknown module \"%.*s\".",
  /* LYE_XPATH_INFUNC */ "Unknown XPath function \"%.*s\".",
  /* LYE_XPATH_INARGCOUNT */ "Invalid number of arguments (%d) for the XPath function %.*s.",
  /* LYE_XPATH_INARGTYPE */ "Wrong type of argument #%d (%s) for the XPath function %s.",
  /* LYE_XPATH_DUMMY */   "Accessing the value of the dummy node \"%s\".",
  /* LYE_XPATH_NOEND */   "Unterminated string delimited with %c (%.15s).",
  
  /* LYE_PATH_INCHAR */  "Unexpected character(s) '%c' (\"%s\").",
  /* LYE_PATH_INMOD */   "Module not found.",
  /* LYE_PATH_MISSMOD */ "Missing module name.",
  /* LYE_PATH_INNODE */  "Schema node not found.",
  /* LYE_PATH_INKEY */   "List key not found or on incorrect position (\"%s\").",
  /* LYE_PATH_MISSKEY */ "List keys or position missing (\"%s\").",
  /* LYE_PATH_EXISTS */  "Node already exists.",
  /* LYE_PATH_MISSPAR */ "Parent does not exist.",
  };
  
  static const LY_VECODE ecode2vecode[] = {
      LYVE_SUCCESS,      /* LYE_SUCCESS */
  
      LYVE_XML_MISS,     /* LYE_XML_MISS */
      LYVE_XML_INVAL,    /* LYE_XML_INVAL */
      LYVE_XML_INCHAR,   /* LYE_XML_INCHAR */
  
      LYVE_EOF,          /* LYE_EOF */
      LYVE_INSTMT,       /* LYE_INSTMT */
      LYVE_INSTMT,       /* LYE_INCHILDSTMT */
      LYVE_INPAR,        /* LYE_INPAR */
      LYVE_INID,         /* LYE_INID */
      LYVE_INDATE,       /* LYE_INDATE */
      LYVE_INARG,        /* LYE_INARG */
      LYVE_MISSSTMT,     /* LYE_MISSCHILDSTMT */
      LYVE_MISSSTMT,     /* LYE_MISSSTMT */
      LYVE_MISSARG,      /* LYE_MISSARG */
      LYVE_TOOMANY,      /* LYE_TOOMANY */
      LYVE_DUPID,        /* LYE_DUPID */
      LYVE_DUPLEAFLIST,  /* LYE_DUPLEAFLIST */
      LYVE_DUPLIST,      /* LYE_DUPLIST */
      LYVE_NOUNIQ,       /* LYE_NOUNIQ */
      LYVE_ENUM_INVAL,   /* LYE_ENUM_INVAL */
      LYVE_ENUM_INNAME,  /* LYE_ENUM_INNAME */
      LYVE_ENUM_INVAL,   /* LYE_ENUM_DUPVAL */
      LYVE_ENUM_INNAME,  /* LYE_ENUM_DUPNAME */
      LYVE_ENUM_WS,      /* LYE_ENUM_WS */
      LYVE_BITS_INVAL,   /* LYE_BITS_INVAL */
      LYVE_BITS_INNAME,  /* LYE_BITS_INNAME */
      LYVE_BITS_INVAL,   /* LYE_BITS_DUPVAL */
      LYVE_BITS_INNAME,  /* LYE_BITS_DUPNAME */
      LYVE_INMOD,        /* LYE_INMOD */
      LYVE_INMOD,        /* LYE_INMOD_LEN */
      LYVE_KEY_NLEAF,    /* LYE_KEY_NLEAF */
      LYVE_KEY_TYPE,     /* LYE_KEY_TYPE */
      LYVE_KEY_CONFIG,   /* LYE_KEY_CONFIG */
      LYVE_KEY_MISS,     /* LYE_KEY_MISS */
      LYVE_KEY_DUP,      /* LYE_KEY_DUP */
      LYVE_INREGEX,      /* LYE_INREGEX */
      LYVE_INRESOLV,     /* LYE_INRESOLV */
      LYVE_INSTATUS,     /* LYE_INSTATUS */
      LYVE_CIRC_LEAFREFS,/* LYE_CIRC_LEAFREFS */
      LYVE_CIRC_FEATURES,/* LYE_CIRC_FEATURES */
      LYVE_CIRC_IMPORTS, /* LYE_CIRC_IMPORTS */
      LYVE_CIRC_INCLUDES,/* LYE_CIRC_INCLUDES */
      LYVE_INVER,        /* LYE_INVER */
      LYVE_SUBMODULE,    /* LYE_SUBMODULE */
  
      LYVE_OBSDATA,      /* LYE_OBSDATA */
      LYVE_OBSDATA,      /* LYE_OBSTYPE */
      LYVE_NORESOLV,     /* LYE_NORESOLV */
      LYVE_INELEM,       /* LYE_INELEM */
      LYVE_INELEM,       /* LYE_INELEM_LEN */
      LYVE_MISSELEM,     /* LYE_MISSELEM */
      LYVE_INVAL,        /* LYE_INVAL */
      LYVE_INMETA,       /* LYE_INMETA */
      LYVE_INATTR,       /* LYE_INATTR */
      LYVE_MISSATTR,     /* LYE_MISSATTR */
      LYVE_NOCONSTR,     /* LYE_NOCONSTR */
      LYVE_INCHAR,       /* LYE_INCHAR */
      LYVE_INPRED,       /* LYE_INPRED */
      LYVE_MCASEDATA,    /* LYE_MCASEDATA */
      LYVE_NOMUST,       /* LYE_NOMUST */
      LYVE_NOWHEN,       /* LYE_NOWHEN */
      LYVE_INORDER,      /* LYE_INORDER */
      LYVE_INWHEN,       /* LYE_INWHEN */
      LYVE_NOMIN,        /* LYE_NOMIN */
      LYVE_NOMAX,        /* LYE_NOMAX */
      LYVE_NOREQINS,     /* LYE_NOREQINS */
      LYVE_NOLEAFREF,    /* LYE_NOLEAFREF */
      LYVE_NOMANDCHOICE, /* LYE_NOMANDCHOICE */
  
      LYVE_XPATH_INTOK,  /* LYE_XPATH_INTOK */
      LYVE_XPATH_EOF,    /* LYE_XPATH_EOF */
      LYVE_XPATH_INOP,   /* LYE_XPATH_INOP_1 */
      LYVE_XPATH_INOP,   /* LYE_XPATH_INOP_2 */
      LYVE_XPATH_INCTX,  /* LYE_XPATH_INCTX */
      LYVE_XPATH_INMOD,  /* LYE_XPATH_INMOD */
      LYVE_XPATH_INFUNC, /* LYE_XPATH_INFUNC */
      LYVE_XPATH_INARGCOUNT, /* LYE_XPATH_INARGCOUNT */
      LYVE_XPATH_INARGTYPE, /* LYE_XPATH_INARGTYPE */
      LYVE_XPATH_DUMMY,  /* LYE_XPATH_DUMMY */
      LYVE_XPATH_NOEND,  /* LYE_XPATH_NOEND */
  
      LYVE_PATH_INCHAR,  /* LYE_PATH_INCHAR */
      LYVE_PATH_INMOD,   /* LYE_PATH_INMOD */
      LYVE_PATH_MISSMOD, /* LYE_PATH_MISSMOD */
      LYVE_PATH_INNODE,  /* LYE_PATH_INNODE */
      LYVE_PATH_INKEY,   /* LYE_PATH_INKEY */
      LYVE_PATH_MISSKEY, /* LYE_PATH_MISSKEY */
      LYVE_PATH_EXISTS,  /* LYE_PATH_EXISTS */
      LYVE_PATH_MISSPAR, /* LYE_PATH_MISSPAR */
  };
  
  
  void
  ly_vlog_hide(uint8_t hide)
  {
      ly_err_main.vlog_hide = hide;
  }
  
  static int
  ly_vlog_build_path_reverse_print(char **path, uint16_t *index, const char *str, uint16_t str_len, uint16_t *length, int enlarge)
  {
      void *mem;
  
      if ((*index) < str_len) {
          if (enlarge) {
              /* enlarge buffer */
              mem = realloc(*path, *length + *index + LY_BUF_SIZE);
              LY_CHECK_ERR_RETURN(!mem, LOGMEM, -1);
              *path = mem;
  
              /* move data, lengths */
              memmove(&(*path)[*index], &(*path)[*index + LY_BUF_SIZE], *length);
              (*index) += LY_BUF_SIZE;
          } else {
              LOGERR(LY_SUCCESS, "%s: path is too long.", __func__);
              return -1;
          }
      }
  
      (*index) -= str_len;
      memcpy(&(*path)[*index], str, str_len);
      if (length) {
          *length += str_len;
      }
  
      return 0;
  }
  
  int
  ly_vlog_build_path_reverse(enum LY_VLOG_ELEM elem_type, const void *elem, char **path, uint16_t *index, uint16_t *length,
                             int enlarge)
  {
      int i, j;
      struct lys_node_list *slist;
      struct lys_node *sparent = NULL;
      struct lyd_node *dlist, *diter;
      const struct lys_module *top_smodule = NULL;
      const char *name, *prefix = NULL, *val_end, *val_start;
      char *str;
      size_t len;
  
      if (length) {
          *length = 0;
      } else if (enlarge) {
          /* we need to use length if we are to enlarge path */
          LOGINT;
          return -1;
      }
  
      while (elem) {
          switch (elem_type) {
          case LY_VLOG_XML:
              name = ((struct lyxml_elem *)elem)->name;
              prefix = ((struct lyxml_elem *)elem)->ns ? ((struct lyxml_elem *)elem)->ns->prefix : NULL;
              elem = ((struct lyxml_elem *)elem)->parent;
              break;
          case LY_VLOG_LYS:
              if (!top_smodule) {
                  /* remember the top module, it will act as the current module */
                  top_smodule = lys_node_module((struct lys_node *)elem);
              }
  
              if (((struct lys_node *)elem)->nodetype & (LYS_AUGMENT | LYS_GROUPING)) {
                  if (ly_vlog_build_path_reverse_print(path, index, "]", 1, length, enlarge)) {
                      return -1;
                  }
  
                  name = ((struct lys_node *)elem)->name;
                  if (ly_vlog_build_path_reverse_print(path, index, name, strlen(name), length, enlarge)) {
                      return -1;
                  }
  
                  if (((struct lys_node *)elem)->nodetype == LYS_GROUPING) {
                      name = "{grouping}[";
                  } else { /* augment */
                      name = "{augment}[";
                  }
              } else if (((struct lys_node *)elem)->nodetype == LYS_EXT) {
                  name = ((struct lys_ext_instance *)elem)->def->name;
              } else {
                  name = ((struct lys_node *)elem)->name;
              }
  
              if (!((struct lys_node *)elem)->parent || lys_node_module((struct lys_node *)elem) != top_smodule) {
                  prefix = lys_node_module((struct lys_node *)elem)->name;
              } else {
                  prefix = NULL;
              }
              if (((struct lys_node *)elem)->nodetype == LYS_EXT) {
                  if (((struct lys_ext_instance*)elem)->parent_type == LYEXT_PAR_NODE) {
                      elem = (struct lys_node*)((struct lys_ext_instance*)elem)->parent;
                  } else {
                      sparent = NULL;
                      elem = NULL;
                  }
                  break;
              }
  
              /* need to find the parent again because we don't want to skip augments */
              do {
                  sparent = ((struct lys_node *)elem)->parent;
                  elem = lys_parent((struct lys_node *)elem);
              } while (elem && (((struct lys_node *)elem)->nodetype == LYS_USES));
              break;
          case LY_VLOG_LYD:
              name = ((struct lyd_node *)elem)->schema->name;
              if (!((struct lyd_node *)elem)->parent ||
                      lyd_node_module((struct lyd_node *)elem) != lyd_node_module(((struct lyd_node *)elem)->parent)) {
                  prefix = lyd_node_module((struct lyd_node *)elem)->name;
              } else {
                  prefix = NULL;
              }
  
              /* handle predicates (keys) in case of lists */
              if (((struct lyd_node *)elem)->schema->nodetype == LYS_LIST) {
                  dlist = (struct lyd_node *)elem;
                  slist = (struct lys_node_list *)((struct lyd_node *)elem)->schema;
                  if (slist->keys_size) {
                      /* schema list with keys - use key values in predicates */
                      for (i = slist->keys_size - 1; i > -1; i--) {
                          LY_TREE_FOR(dlist->child, diter) {
                              if (diter->schema == (struct lys_node *)slist->keys[i]) {
                                  break;
                              }
                          }
                          if (diter && ((struct lyd_node_leaf_list *)diter)->value_str) {
                              if (strchr(((struct lyd_node_leaf_list *)diter)->value_str, '\'')) {
                                  val_start = "=\"";
                                  val_end = "\"]";
                              } else {
                                  val_start = "='";
                                  val_end = "']";
                              }
  
                              /* print value */
                              if (ly_vlog_build_path_reverse_print(path, index, val_end, 2, length, enlarge)) {
                                  return -1;
                              }
                              len = strlen(((struct lyd_node_leaf_list *)diter)->value_str);
                              if (ly_vlog_build_path_reverse_print(path, index,
                                      ((struct lyd_node_leaf_list *)diter)->value_str, len, length, enlarge)) {
                                  return -1;
                              }
  
                              /* print schema name */
                              if (ly_vlog_build_path_reverse_print(path, index, val_start, 2, length, enlarge)) {
                                  return -1;
                              }
                              len = strlen(diter->schema->name);
                              if (ly_vlog_build_path_reverse_print(path, index, diter->schema->name, len, length, enlarge)) {
                                  return -1;
                              }
  
                              if (lyd_node_module(dlist) != lyd_node_module(diter)) {
                                  if (ly_vlog_build_path_reverse_print(path, index, ":", 1, length, enlarge)) {
                                      return -1;
                                  }
                                  len = strlen(lyd_node_module(diter)->name);
                                  if (ly_vlog_build_path_reverse_print(path, index, lyd_node_module(diter)->name, len, length, enlarge)) {
                                      return -1;
                                  }
                              }
  
                              if (ly_vlog_build_path_reverse_print(path, index, "[", 1, length, enlarge)) {
                                  return -1;
                              }
                          }
                      }
                  } else {
                      /* schema list without keys - use instance position */
                      i = j = lyd_list_pos(dlist);
                      len = 1;
                      while (j > 9) {
                          ++len;
                          j /= 10;
                      }
  
                      if (ly_vlog_build_path_reverse_print(path, index, "]", 1, length, enlarge)) {
                          return -1;
                      }
  
                      str = malloc(len + 1);
                      LY_CHECK_ERR_RETURN(!str, LOGMEM, -1);
                      sprintf(str, "%d", i);
  
                      if (ly_vlog_build_path_reverse_print(path, index, str, len, length, enlarge)) {
                          free(str);
                          return -1;
                      }
                      free(str);
  
                      if (ly_vlog_build_path_reverse_print(path, index, "[", 1, length, enlarge)) {
                          return -1;
                      }
                  }
              } else if (((struct lyd_node *)elem)->schema->nodetype == LYS_LEAFLIST &&
                      ((struct lyd_node_leaf_list *)elem)->value_str) {
                  if (strchr(((struct lyd_node_leaf_list *)elem)->value_str, '\'')) {
                      val_start = "[.=\"";
                      val_end = "\"]";
                  } else {
                      val_start = "[.='";
                      val_end = "']";
                  }
  
                  if (ly_vlog_build_path_reverse_print(path, index, val_end, 2, length, enlarge)) {
                      return -1;
                  }
                  len = strlen(((struct lyd_node_leaf_list *)elem)->value_str);
                  if (ly_vlog_build_path_reverse_print(path, index, ((struct lyd_node_leaf_list *)elem)->value_str, len, length, enlarge)) {
                      return -1;
                  }
                  if (ly_vlog_build_path_reverse_print(path, index, val_start, 4, length, enlarge)) {
                      return -1;
                  }
              }
  
              elem = ((struct lyd_node *)elem)->parent;
              break;
          case LY_VLOG_STR:
              i = 0;
              len = strlen((const char *)elem) + 1;
              if (ly_vlog_build_path_reverse_print(path, index, (const char *)elem, len, length, enlarge)) {
                  i = -1;
                  if (!enlarge) {
                      /* print at least what we can */
                      len = (*index);
                      ly_vlog_build_path_reverse_print(path, index, (const char *)elem, len, length, enlarge);
                  }
              }
              return i;
          default:
              /* shouldn't be here */
              LOGINT;
              return -1;
          }
          if (name) {
              if (ly_vlog_build_path_reverse_print(path, index, name, strlen(name), length, enlarge)) {
                  return -1;
              }
              if (prefix) {
                  if (ly_vlog_build_path_reverse_print(path, index, ":", 1, length, enlarge)) {
                      return -1;
                  }
                  if (ly_vlog_build_path_reverse_print(path, index, prefix, strlen(prefix), length, enlarge)) {
                      return -1;
                  }
              }
          }
          if (ly_vlog_build_path_reverse_print(path, index, "/", 1, length, enlarge)) {
              return -1;
          }
          if ((elem_type == LY_VLOG_LYS) && !elem && sparent && (sparent->nodetype == LYS_AUGMENT)) {
              len = strlen(((struct lys_node_augment *)sparent)->target_name);
              if (ly_vlog_build_path_reverse_print(path, index, ((struct lys_node_augment *)sparent)->target_name, len, length, enlarge)) {
                  return -1;
              }
          }
      }
  
      return 0;
  }
  
  void
  ly_vlog(LY_ECODE code, enum LY_VLOG_ELEM elem_type, const void *elem, ...)
  {
      va_list ap;
      const char *fmt;
      char* path = NULL;
      uint16_t *index = NULL;
  
      ly_errno = LY_EVALID;
  
      if ((code == LYE_PATH) && !path_flag) {
          return;
      }
      if (code > 0) {
          ly_vecode = ecode2vecode[code];
      }
  
      if (!path_flag) {
          goto log;
      }
  
      /* resolve path */
      path = ((struct ly_err *)&ly_errno)->path;
      index = &((struct ly_err *)&ly_errno)->path_index;
      if ((elem_type != LY_VLOG_NONE) && (elem_type != LY_VLOG_PREV)) { /* != LY_VLOG_NONE */
          /* update path */
          (*index) = LY_BUF_SIZE - 1;
          path[(*index)] = '\0';
          if (!elem) {
              /* top-level */
              path[--(*index)] = '/';
          } else {
              ly_vlog_build_path_reverse(elem_type, elem, &path, index, NULL, 0);
          }
      } else if (elem_type == LY_VLOG_NONE) {
          /* erase path, the rest will be erased by log_vprintf() since it will get NULL path parameter */
          path[(*index)] = '\0';
      }
  
  log:
      va_start(ap, elem);
      switch (code) {
      case LYE_SPEC:
          fmt = va_arg(ap, char *);
          log_vprintf(LY_LLERR, ly_err_main.vlog_hide, fmt, index && path[(*index)] ? &path[(*index)] : NULL, ap);
          break;
      case LYE_PATH:
          log_vprintf(LY_LLERR, ly_err_main.vlog_hide, NULL, &path[(*index)], ap);
          break;
      default:
          log_vprintf(LY_LLERR, ly_err_main.vlog_hide, ly_errs[code],
                      index && path[(*index)] ? &path[(*index)] : NULL, ap);
          break;
      }
      va_end(ap);
  }
  
  void
  ly_err_repeat(struct ly_ctx *ctx)
  {
      struct ly_err_item *i;
  
      if ((ly_log_level >= LY_LLERR) && !ly_err_main.vlog_hide) {
          for (i = pthread_getspecific(ctx->errlist_key); i; i = i->next) {
              if (ly_log_clb) {
                  ly_log_clb(LY_LLERR, i->msg, i->path);
              } else {
                  fprintf(stderr, "libyang[%d]: %s%s", LY_LLERR, i->msg, i->path ? " " : "\n");
                  if (i->path) {
                      fprintf(stderr, "(path: %s)\n", i->path);
                  }
              }
          }
      }
  }
  