logger CHANGE support for validation errors - work in progress
diff --git a/src/log.c b/src/log.c
index d61895e..d1a6dd3 100644
--- a/src/log.c
+++ b/src/log.c
@@ -30,6 +30,9 @@
 volatile int ly_log_dbg_groups = 0;
 #endif
 
+/* how many bytes add when enlarging buffers */
+#define LY_BUF_STEP 128
+
 API LY_VECODE
 ly_vecode(const struct ly_ctx *ctx)
 {
@@ -369,6 +372,259 @@
     va_end(ap);
 }
 
+static LY_ERR
+ly_vlog_build_path_print(const struct ly_ctx *ctx, char **path, uint16_t *index, const char *str, uint16_t str_len, uint16_t *length)
+{
+    void *mem;
+    uint16_t step;
+
+    if ((*index) < str_len) {
+        /* enlarge buffer */
+        step = (str_len < LY_BUF_STEP) ? LY_BUF_STEP : str_len;
+        mem = realloc(*path, *length + *index + step + 1);
+        LY_CHECK_ERR_RET(!mem, LOGMEM(ctx), LY_EMEM);
+        *path = mem;
+
+        /* move data, lengths */
+        memmove(&(*path)[*index + step], &(*path)[*index], *length);
+        (*index) += step;
+    }
+
+    (*index) -= str_len;
+    memcpy(&(*path)[*index], str, str_len);
+    *length += str_len;
+
+    return 0;
+}
+
+LY_ERR
+ly_vlog_build_path(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, char **path, int UNUSED(schema_all_prefixes))
+{
+    uint16_t length, index;
+    size_t len;
+    LY_ERR rc = LY_SUCCESS;
+
+    length = 0;
+    *path = malloc(1);
+    LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
+    index = 0;
+
+    while (elem) {
+        switch (elem_type) {
+        case LY_VLOG_LYS:
+            /* TODO */
+            break;
+        case LY_VLOG_LYD:
+#if 0 /* TODO when data tree present */
+            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_print(path, &index, val_end, 2, &length)) {
+                                return -1;
+                            }
+                            len = strlen(((struct lyd_node_leaf_list *)diter)->value_str);
+                            if (ly_vlog_build_path_print(path, &index,
+                                    ((struct lyd_node_leaf_list *)diter)->value_str, len, &length)) {
+                                return -1;
+                            }
+
+                            /* print schema name */
+                            if (ly_vlog_build_path_print(path, &index, val_start, 2, &length)) {
+                                return -1;
+                            }
+                            len = strlen(diter->schema->name);
+                            if (ly_vlog_build_path_print(path, &index, diter->schema->name, len, &length)) {
+                                return -1;
+                            }
+
+                            if (lyd_node_module(dlist) != lyd_node_module(diter)) {
+                                if (ly_vlog_build_path_print(path, &index, ":", 1, &length)) {
+                                    return -1;
+                                }
+                                len = strlen(lyd_node_module(diter)->name);
+                                if (ly_vlog_build_path_print(path, &index, lyd_node_module(diter)->name, len, &length)) {
+                                    return -1;
+                                }
+                            }
+
+                            if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) {
+                                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_print(path, &index, "]", 1, &length)) {
+                        return -1;
+                    }
+
+                    str = malloc(len + 1);
+                    LY_CHECK_ERR_RETURN(!str, LOGMEM(NULL), -1);
+                    sprintf(str, "%d", i);
+
+                    if (ly_vlog_build_path_print(path, &index, str, len, &length)) {
+                        free(str);
+                        return -1;
+                    }
+                    free(str);
+
+                    if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) {
+                        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_print(path, &index, val_end, 2, &length)) {
+                    return -1;
+                }
+                len = strlen(((struct lyd_node_leaf_list *)elem)->value_str);
+                if (ly_vlog_build_path_print(path, &index, ((struct lyd_node_leaf_list *)elem)->value_str, len, &length)) {
+                    return -1;
+                }
+                if (ly_vlog_build_path_print(path, &index, val_start, 4, &length)) {
+                    return -1;
+                }
+            }
+
+            /* check if it is yang-data top element */
+            if (!((struct lyd_node *)elem)->parent) {
+                ext_name = lyp_get_yang_data_template_name(elem);
+                if (ext_name) {
+                    if (ly_vlog_build_path_print(path, &index, name, strlen(name), &length)) {
+                        return -1;
+                    }
+                    if (ly_vlog_build_path_print(path, &index, "/", 1, &length)) {
+                        return -1;
+                    }
+                    yang_data_extension = 1;
+                    name = ext_name;
+               }
+            }
+
+            elem = ((struct lyd_node *)elem)->parent;
+            break;
+#endif
+        case LY_VLOG_STR:
+            len = strlen((const char *)elem);
+            rc = ly_vlog_build_path_print(ctx, path, &index, (const char *)elem, len, &length);
+            LY_CHECK_RET(rc != LY_SUCCESS, rc);
+            goto success;
+        case LY_VLOG_LINE:
+
+            goto success;
+        default:
+            /* shouldn't be here */
+            LOGINT_RET(ctx);
+        }
+
+#if 0 /* TODO when data/schema tree present */
+        if (name) {
+            if (ly_vlog_build_path_print(ctx, path, &index, name, strlen(name), &length)) {
+                return -1;
+            }
+            if (prefix) {
+                if (yang_data_extension && ly_vlog_build_path_print(path, &index, "#", 1, &length)) {
+                    return -1;
+                }
+                if (ly_vlog_build_path_print(ctx, path, &index, ":", 1, &length)) {
+                    return -1;
+                }
+                if (ly_vlog_build_path_print(ctx, path, &index, prefix, strlen(prefix), &length)) {
+                    return -1;
+                }
+            }
+        }
+        if (ly_vlog_build_path_print(ctx, path, &index, "/", 1, &length)) {
+            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_print(ctx, path, &index, ((struct lys_node_augment *)sparent)->target_name, len, &length)) {
+                return -1;
+            }
+        }
+#endif
+    }
+
+success:
+    memmove(*path, (*path) + index, length);
+    (*path)[length] = '\0';
+    return LY_SUCCESS;
+}
+
+void
+ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
+{
+    va_list ap;
+    char* path = NULL;
+    const struct ly_err_item *first;
+
+    if (path_flag && (elem_type != LY_VLOG_NONE)) {
+        if (elem_type == LY_VLOG_PREV) {
+            /* use previous path */
+            first = ly_err_first(ctx);
+            if (first && first->prev->path) {
+                path = strdup(first->prev->path);
+            }
+        } else {
+            /* print path */
+            if (!elem) {
+                /* top-level */
+                path = strdup("/");
+            } else {
+                ly_vlog_build_path(ctx, elem_type, elem, &path, 0);
+            }
+        }
+    }
+
+    va_start(ap, format);
+    log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
+    /* path is spent and should not be freed! */
+    va_end(ap);
+}
+
 API void
 ly_err_print(struct ly_err_item *eitem)
 {