tree schema FEATURE data path with key value placeholders

Refs #1818
diff --git a/src/tree_schema.c b/src/tree_schema.c
index c7f75ef..1ac76bc 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -725,76 +725,93 @@
 lysc_path_until(const struct lysc_node *node, const struct lysc_node *parent, LYSC_PATH_TYPE pathtype, char *buffer,
         size_t buflen)
 {
-    const struct lysc_node *iter, *par;
+    const struct lysc_node *iter, *par, *key;
     char *path = NULL;
     int len = 0;
+    ly_bool skip_schema;
 
     if (buffer) {
         LY_CHECK_ARG_RET(node->module->ctx, buflen > 1, NULL);
         buffer[0] = '\0';
     }
 
-    switch (pathtype) {
-    case LYSC_PATH_LOG:
-    case LYSC_PATH_DATA:
-        for (iter = node; iter && (iter != parent) && (len >= 0); iter = iter->parent) {
-            char *s, *id;
-            const char *slash;
+    if ((pathtype == LYSC_PATH_DATA) || (pathtype == LYSC_PATH_DATA_PATTERN)) {
+        /* skip schema-only nodes */
+        skip_schema = 1;
+    } else {
+        skip_schema = 0;
+    }
 
-            if ((pathtype == LYSC_PATH_DATA) && (iter->nodetype & (LYS_CHOICE | LYS_CASE | LYS_INPUT | LYS_OUTPUT))) {
-                /* schema-only node */
-                continue;
-            }
+    for (iter = node; iter && (iter != parent) && (len >= 0); iter = iter->parent) {
+        char *s;
+        const char *slash;
 
-            s = buffer ? strdup(buffer) : path;
-            id = strdup(iter->name);
-            if (parent && (iter->parent == parent)) {
-                slash = "";
-            } else {
-                slash = "/";
-            }
+        if (skip_schema && (iter->nodetype & (LYS_CHOICE | LYS_CASE | LYS_INPUT | LYS_OUTPUT))) {
+            /* schema-only node */
+            continue;
+        }
 
-            if (pathtype == LYSC_PATH_DATA) {
-                par = lysc_data_parent(iter);
-            } else {
-                par = iter->parent;
-            }
+        if ((pathtype == LYSC_PATH_DATA_PATTERN) && (iter->nodetype == LYS_LIST)) {
+            key = NULL;
+            while ((key = lys_getnext(key, iter, NULL, 0)) && lysc_is_key(key)) {
+                s = buffer ? strdup(buffer) : path;
 
-            if (!par || (par->module != iter->module)) {
-                /* print prefix */
+                /* print key predicate */
                 if (buffer) {
-                    len = snprintf(buffer, buflen, "%s%s:%s%s", slash, iter->module->name, id, s ? s : "");
+                    len = snprintf(buffer, buflen, "[%s='%%s']%s", key->name, s ? s : "");
                 } else {
-                    len = asprintf(&path, "%s%s:%s%s", slash, iter->module->name, id, s ? s : "");
+                    len = asprintf(&path, "[%s='%%s']%s", key->name, s ? s : "");
                 }
-            } else {
-                /* prefix is the same as in parent */
-                if (buffer) {
-                    len = snprintf(buffer, buflen, "%s%s%s", slash, id, s ? s : "");
-                } else {
-                    len = asprintf(&path, "%s%s%s", slash, id, s ? s : "");
-                }
-            }
-            free(s);
-            free(id);
 
-            if (buffer && (buflen <= (size_t)len)) {
-                /* not enough space in buffer */
-                break;
+                free(s);
             }
         }
 
-        if (len < 0) {
-            free(path);
-            path = NULL;
-        } else if (len == 0) {
+        s = buffer ? strdup(buffer) : path;
+        if (parent && (iter->parent == parent)) {
+            slash = "";
+        } else {
+            slash = "/";
+        }
+
+        if (skip_schema) {
+            par = lysc_data_parent(iter);
+        } else {
+            par = iter->parent;
+        }
+
+        if (!par || (par->module != iter->module)) {
+            /* print prefix */
             if (buffer) {
-                strcpy(buffer, "/");
+                len = snprintf(buffer, buflen, "%s%s:%s%s", slash, iter->module->name, iter->name, s ? s : "");
             } else {
-                path = strdup("/");
+                len = asprintf(&path, "%s%s:%s%s", slash, iter->module->name, iter->name, s ? s : "");
+            }
+        } else {
+            /* prefix is the same as in parent */
+            if (buffer) {
+                len = snprintf(buffer, buflen, "%s%s%s", slash, iter->name, s ? s : "");
+            } else {
+                len = asprintf(&path, "%s%s%s", slash, iter->name, s ? s : "");
             }
         }
-        break;
+        free(s);
+
+        if (buffer && (buflen <= (size_t)len)) {
+            /* not enough space in buffer */
+            break;
+        }
+    }
+
+    if (len < 0) {
+        free(path);
+        path = NULL;
+    } else if (len == 0) {
+        if (buffer) {
+            strcpy(buffer, "/");
+        } else {
+            path = strdup("/");
+        }
     }
 
     if (buffer) {
diff --git a/src/tree_schema.h b/src/tree_schema.h
index acfb9bb..9874378 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -2317,7 +2317,9 @@
  */
 typedef enum {
     LYSC_PATH_LOG,  /**< Descriptive path format used in log messages */
-    LYSC_PATH_DATA  /**< Similar to ::LYSC_PATH_LOG except that schema-only nodes (choice, case) are skipped */
+    LYSC_PATH_DATA, /**< Similar to ::LYSC_PATH_LOG except that schema-only nodes (choice, case) are skipped */
+    LYSC_PATH_DATA_PATTERN  /**< Similar to ::LYSC_PATH_DATA but there are predicates for all list keys added with
+                                 "%s" where their values should be so that they can be printed there */
 } LYSC_PATH_TYPE;
 
 /**