structure FEATURE support for augment-structure
diff --git a/src/schema_compile_amend.c b/src/schema_compile_amend.c
index 66e6a3d..e64b183 100644
--- a/src/schema_compile_amend.c
+++ b/src/schema_compile_amend.c
@@ -1539,25 +1539,65 @@
 }
 
 /**
+ * @brief Check whether a compiled ext instance matches a single schema nodeid name test.
+ *
+ * @param[in,out] ext Compiled ext instance to consider. On a match it is zeroed to not match again.
+ * @param[in] mod Expected module.
+ * @param[in] name Expected name.
+ * @param[in] name_len Length of @p name.
+ * @return Whether it is a match or not.
+ */
+static ly_bool
+lysp_schema_nodeid_match_ext(const struct lysc_ext_instance **ext, const struct lys_module *mod, const char *name,
+        size_t name_len)
+{
+    /* compare with the module */
+    if ((*ext)->module != mod) {
+        return 0;
+    }
+
+    /* compare names (argument) */
+    if (ly_strncmp((*ext)->argument, name, name_len)) {
+        return 0;
+    }
+
+    /* zero */
+    *ext = NULL;
+
+    return 1;
+}
+
+/**
  * @brief Check whether a node matches specific schema nodeid.
  *
  * @param[in] exp Parsed nodeid to match.
  * @param[in] exp_pmod Module to use for nodes in @p exp without a prefix.
+ * @param[in] exp_ext Extension instance in which @p exp is defined, it means it targets an extension instance.
  * @param[in] ctx_node Initial context node that should match, only for descendant paths.
  * @param[in] parent First compiled parent to consider. If @p pnode is NULL, it is condered the node to be matched.
  * @param[in] pnode Parsed node to be matched. May be NULL if the target node was already compiled.
  * @param[in] pnode_mod Compiled @p pnode to-be module.
+ * @param[in] pnode_ext Extension instance in which @p pnode is defined.
  * @return Whether it is a match or not.
  */
 static ly_bool
-lysp_schema_nodeid_match(const struct lyxp_expr *exp, const struct lysp_module *exp_pmod, const struct lysc_node *ctx_node,
-        const struct lysc_node *parent, const struct lysp_node *pnode, const struct lys_module *pnode_mod)
+lysp_schema_nodeid_match(const struct lyxp_expr *exp, const struct lysp_module *exp_pmod,
+        const struct lysp_ext_instance *exp_ext, const struct lysc_node *ctx_node, const struct lysc_node *parent,
+        const struct lysp_node *pnode, const struct lys_module *pnode_mod, const struct lysc_ext_instance *pnode_ext)
 {
     uint32_t i;
     const struct lys_module *mod;
     const char *name = NULL;
     size_t name_len = 0;
 
+    if (exp_ext && !pnode_ext) {
+        /* extension instance augment and standard node, will never match */
+        return 0;
+    } else if (!exp_ext && pnode_ext) {
+        /* standard augment and extension instance node, will never match */
+        return 0;
+    }
+
     /* compare last node in the node ID */
     i = exp->used - 1;
 
@@ -1582,7 +1622,7 @@
         i -= 2;
         assert(exp->tokens[i] == LYXP_TOKEN_NAMETEST);
 
-        if (!parent) {
+        if (!parent && !pnode_ext) {
             /* no more parents but path continues */
             return 0;
         }
@@ -1592,17 +1632,24 @@
                 &name_len);
         assert(mod);
 
-        /* compare with the parent */
-        if (!lysp_schema_nodeid_match_node(&parent, mod, name, name_len)) {
-            return 0;
+        if (parent) {
+            /* compare with the parent */
+            if (!lysp_schema_nodeid_match_node(&parent, mod, name, name_len)) {
+                return 0;
+            }
+        } else {
+            /* compare with the ext instance */
+            if (!lysp_schema_nodeid_match_ext(&pnode_ext, mod, name, name_len)) {
+                return 0;
+            }
         }
     }
 
     if (ctx_node && (ctx_node != parent)) {
         /* descendant path has not finished in the context node */
         return 0;
-    } else if (!ctx_node && parent) {
-        /* some parent was not matched */
+    } else if (!ctx_node && (parent || pnode_ext)) {
+        /* some parent/extension was not matched */
         return 0;
     }
 
@@ -1710,7 +1757,8 @@
     for (i = 0; i < ctx->uses_rfns.count; ) {
         rfn = ctx->uses_rfns.objs[i];
 
-        if (!lysp_schema_nodeid_match(rfn->nodeid, rfn->nodeid_pmod, rfn->nodeid_ctx_node, parent, pnode, ctx->cur_mod)) {
+        if (!lysp_schema_nodeid_match(rfn->nodeid, rfn->nodeid_pmod, NULL, rfn->nodeid_ctx_node, parent, pnode,
+                ctx->cur_mod, ctx->ext)) {
             /* not our target node */
             ++i;
             continue;
@@ -1736,7 +1784,7 @@
     for (i = 0; i < ctx->devs.count; ++i) {
         dev = ctx->devs.objs[i];
 
-        if (!lysp_schema_nodeid_match(dev->nodeid, dev->dev_pmods[0], NULL, parent, pnode, ctx->cur_mod)) {
+        if (!lysp_schema_nodeid_match(dev->nodeid, dev->dev_pmods[0], NULL, NULL, parent, pnode, ctx->cur_mod, ctx->ext)) {
             /* not our target node */
             continue;
         }
@@ -1775,7 +1823,19 @@
     return ret;
 }
 
-LY_ERR
+/**
+ * @brief Compile augment children.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] aug_when Parsed augment when to inherit.
+ * @param[in] aug_flags Parsed augment flags.
+ * @param[in] child First augment child to compile.
+ * @param[in] target Target node of the augment.
+ * @param[in] child_unres_disabled Whether the children are to be put into unres disabled set or not.
+ * @return LY_SUCCESS on success.
+ * @return LY_EVALID on failure.
+ */
+static LY_ERR
 lys_compile_augment_children(struct lysc_ctx *ctx, struct lysp_when *aug_when, uint16_t aug_flags, struct lysp_node *child,
         struct lysc_node *target, ly_bool child_unres_disabled)
 {
@@ -1816,9 +1876,9 @@
             } else {
                 ctx->compile_opts |= LYS_COMPILE_RPC_OUTPUT;
             }
-            LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, target, 0, &child_set), cleanup);
+            LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, target, aug_flags, &child_set), cleanup);
         } else {
-            LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, target, 0, &child_set), cleanup);
+            LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, target, aug_flags, &child_set), cleanup);
         }
 
         /* eval if-features again for the rest of this node processing */
@@ -1945,7 +2005,8 @@
     for (i = 0; i < ctx->uses_augs.count; ) {
         aug = ctx->uses_augs.objs[i];
 
-        if (!lysp_schema_nodeid_match(aug->nodeid, orig_mod->parsed, aug->nodeid_ctx_node, node, NULL, NULL)) {
+        if (!lysp_schema_nodeid_match(aug->nodeid, orig_mod->parsed, aug->ext, aug->nodeid_ctx_node, node, NULL, NULL,
+                ctx->ext)) {
             /* not our target node */
             ++i;
             continue;
@@ -1973,7 +2034,7 @@
     for (i = 0; i < ctx->augs.count; ) {
         aug = ctx->augs.objs[i];
 
-        if (!lysp_schema_nodeid_match(aug->nodeid, aug->aug_pmod, NULL, node, NULL, NULL)) {
+        if (!lysp_schema_nodeid_match(aug->nodeid, aug->aug_pmod, aug->ext, NULL, node, NULL, NULL, ctx->ext)) {
             /* not our target node */
             ++i;
             continue;
@@ -2006,15 +2067,17 @@
 }
 
 /**
- * @brief Prepare a top-level augment to be applied during data nodes compilation.
+ * @brief Prepare an absolute-nodeid augment to be applied during data nodes compilation.
  *
  * @param[in] ctx Compile context.
  * @param[in] aug_p Parsed augment to be applied.
  * @param[in] pmod Both current and prefix module for @p aug_p.
+ * @param[in] ext Extension instance in case @p aug_p is defined in one.
  * @return LY_ERR value.
  */
 static LY_ERR
-lys_precompile_own_augment(struct lysc_ctx *ctx, struct lysp_node_augment *aug_p, const struct lysp_module *pmod)
+lys_precompile_own_augment(struct lysc_ctx *ctx, struct lysp_node_augment *aug_p, const struct lysp_module *pmod,
+        const struct lysp_ext_instance *ext)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyxp_expr *exp = NULL;
@@ -2040,6 +2103,7 @@
     aug->nodeid = exp;
     exp = NULL;
     aug->aug_pmod = pmod;
+    aug->ext = ext;
     aug->aug_p = aug_p;
 
 cleanup:
@@ -2047,25 +2111,61 @@
     return ret;
 }
 
+/**
+ * @brief Prepare all top-level augments and extension instance augments to be applied during data nodes compilation.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] pmod Parsed mod to use.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_precompile_own_augments_mod(struct lysc_ctx *ctx, const struct lysp_module *pmod)
+{
+    LY_ARRAY_COUNT_TYPE u, v;
+    struct lysp_node_augment *aug_p;
+
+    /* module */
+    LY_LIST_FOR(pmod->augments, aug_p) {
+        LY_CHECK_RET(lys_precompile_own_augment(ctx, aug_p, pmod, NULL));
+    }
+
+    /* parsed extension instances */
+    LY_ARRAY_FOR(pmod->exts, u) {
+        aug_p = NULL;
+        LY_ARRAY_FOR(pmod->exts[u].substmts, v) {
+            if (pmod->exts[u].substmts[v].stmt == LY_STMT_AUGMENT) {
+                aug_p = *(struct lysp_node_augment **)pmod->exts[u].substmts[v].storage;
+                break;
+            }
+        }
+        if (!aug_p) {
+            continue;
+        }
+
+        LY_CHECK_RET(lys_precompile_own_augment(ctx, aug_p, pmod, &pmod->exts[u]));
+    }
+
+    return LY_SUCCESS;
+}
+
 LY_ERR
 lys_precompile_own_augments(struct lysc_ctx *ctx)
 {
     LY_ARRAY_COUNT_TYPE u, v;
+    const struct lys_module *aug_mod;
+    const struct lysp_module *submod;
 
     LY_ARRAY_FOR(ctx->cur_mod->augmented_by, u) {
-        const struct lys_module *aug_mod = ctx->cur_mod->augmented_by[u];
-        struct lysp_node_augment *aug;
+        aug_mod = ctx->cur_mod->augmented_by[u];
 
         /* collect all module augments */
-        LY_LIST_FOR(aug_mod->parsed->augments, aug) {
-            LY_CHECK_RET(lys_precompile_own_augment(ctx, aug, aug_mod->parsed));
-        }
+        LY_CHECK_RET(lys_precompile_own_augments_mod(ctx, aug_mod->parsed));
 
         /* collect all submodules augments */
         LY_ARRAY_FOR(aug_mod->parsed->includes, v) {
-            LY_LIST_FOR(aug_mod->parsed->includes[v].submodule->augments, aug) {
-                LY_CHECK_RET(lys_precompile_own_augment(ctx, aug, (struct lysp_module *)aug_mod->parsed->includes[v].submodule));
-            }
+            submod = (struct lysp_module *)aug_mod->parsed->includes[v].submodule;
+
+            LY_CHECK_RET(lys_precompile_own_augments_mod(ctx, submod));
         }
     }
 
@@ -2245,24 +2345,28 @@
     return 1;
 }
 
-LY_ERR
-lys_precompile_augments_deviations(struct lys_module *mod, struct lys_glob_unres *unres)
+/**
+ * @brief Add references to target modules of top-level augments, deviations, and augments in extension instances
+ * in a module and all its submodules.
+ *
+ * @param[in] pmod Module to process.
+ * @param[in,out] mod_set Module set to add referenced modules into.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lys_precompile_mod_augments_deviations(struct lysp_module *pmod, struct ly_set *mod_set)
 {
-    LY_ERR ret = LY_SUCCESS, r;
+    LY_ERR ret = LY_SUCCESS;
     LY_ARRAY_COUNT_TYPE u, v;
     struct lysc_ctx ctx;
-    struct lysp_module *mod_p;
     struct lys_module *m;
-    struct lysp_submodule *submod;
     struct lysp_node_augment *aug;
-    const char **imp_f, *all_f[] = {"*", NULL};
-    uint32_t i;
-    struct ly_set mod_set = {0}, set = {0};
+    struct ly_set set = {0};
 
-    mod_p = mod->parsed;
-    LYSC_CTX_INIT_PMOD(ctx, mod_p, NULL);
+    LYSC_CTX_INIT_PMOD(ctx, pmod, NULL);
 
-    LY_LIST_FOR(mod_p->augments, aug) {
+    LY_LIST_FOR(pmod->augments, aug) {
         /* get target module */
         lysc_update_path(&ctx, NULL, "{augment}");
         lysc_update_path(&ctx, NULL, aug->nodeid);
@@ -2272,64 +2376,81 @@
         LY_CHECK_GOTO(ret, cleanup);
 
         /* add this module into the target module augmented_by, if not there and implemented */
-        if ((lys_array_add_mod_ref(&ctx, mod, &m->augmented_by) != LY_EEXIST) ||
+        if ((lys_array_add_mod_ref(&ctx, pmod->mod, &m->augmented_by) != LY_EEXIST) ||
                 !lys_precompile_mod_set_is_all_implemented(&set)) {
-            LY_CHECK_GOTO(ret = ly_set_merge(&mod_set, &set, 0, NULL), cleanup);
+            LY_CHECK_GOTO(ret = ly_set_merge(mod_set, &set, 0, NULL), cleanup);
         }
         ly_set_erase(&set, NULL);
     }
 
-    LY_ARRAY_FOR(mod_p->deviations, u) {
+    LY_ARRAY_FOR(pmod->deviations, u) {
         /* get target module */
         lysc_update_path(&ctx, NULL, "{deviation}");
-        lysc_update_path(&ctx, NULL, mod_p->deviations[u].nodeid);
-        ret = lys_nodeid_mod_check(&ctx, mod_p->deviations[u].nodeid, 1, &set, NULL, &m);
+        lysc_update_path(&ctx, NULL, pmod->deviations[u].nodeid);
+        ret = lys_nodeid_mod_check(&ctx, pmod->deviations[u].nodeid, 1, &set, NULL, &m);
         lysc_update_path(&ctx, NULL, NULL);
         lysc_update_path(&ctx, NULL, NULL);
         LY_CHECK_GOTO(ret, cleanup);
 
         /* add this module into the target module deviated_by, if not there and implemented */
-        if ((lys_array_add_mod_ref(&ctx, mod, &m->deviated_by) != LY_EEXIST) ||
+        if ((lys_array_add_mod_ref(&ctx, pmod->mod, &m->deviated_by) != LY_EEXIST) ||
                 !lys_precompile_mod_set_is_all_implemented(&set)) {
-            LY_CHECK_GOTO(ret = ly_set_merge(&mod_set, &set, 0, NULL), cleanup);
+            LY_CHECK_GOTO(ret = ly_set_merge(mod_set, &set, 0, NULL), cleanup);
         }
         ly_set_erase(&set, NULL);
     }
 
-    /* the same for augments and deviations in submodules */
-    LY_ARRAY_FOR(mod_p->includes, v) {
-        submod = mod_p->includes[v].submodule;
-        ctx.pmod = (struct lysp_module *)submod;
-
-        LY_LIST_FOR(submod->augments, aug) {
-            lysc_update_path(&ctx, NULL, "{augment}");
-            lysc_update_path(&ctx, NULL, aug->nodeid);
-            ret = lys_nodeid_mod_check(&ctx, aug->nodeid, 1, &set, NULL, &m);
-            lysc_update_path(&ctx, NULL, NULL);
-            lysc_update_path(&ctx, NULL, NULL);
-            LY_CHECK_GOTO(ret, cleanup);
-
-            if ((lys_array_add_mod_ref(&ctx, mod, &m->augmented_by) != LY_EEXIST) ||
-                    !lys_precompile_mod_set_is_all_implemented(&set)) {
-                LY_CHECK_GOTO(ret = ly_set_merge(&mod_set, &set, 0, NULL), cleanup);
+    LY_ARRAY_FOR(pmod->exts, u) {
+        aug = NULL;
+        LY_ARRAY_FOR(pmod->exts[u].substmts, v) {
+            if (pmod->exts[u].substmts[v].stmt == LY_STMT_AUGMENT) {
+                aug = *(struct lysp_node_augment **)pmod->exts[u].substmts[v].storage;
+                break;
             }
-            ly_set_erase(&set, NULL);
+        }
+        if (!aug) {
+            continue;
         }
 
-        LY_ARRAY_FOR(submod->deviations, u) {
-            lysc_update_path(&ctx, NULL, "{deviation}");
-            lysc_update_path(&ctx, NULL, submod->deviations[u].nodeid);
-            ret = lys_nodeid_mod_check(&ctx, submod->deviations[u].nodeid, 1, &set, NULL, &m);
-            lysc_update_path(&ctx, NULL, NULL);
-            lysc_update_path(&ctx, NULL, NULL);
-            LY_CHECK_GOTO(ret, cleanup);
+        /* get target module */
+        lysc_update_path(&ctx, NULL, "{ext-augment}");
+        lysc_update_path(&ctx, NULL, aug->nodeid);
+        ret = lys_nodeid_mod_check(&ctx, aug->nodeid, 1, &set, NULL, &m);
+        lysc_update_path(&ctx, NULL, NULL);
+        lysc_update_path(&ctx, NULL, NULL);
+        LY_CHECK_GOTO(ret, cleanup);
 
-            if ((lys_array_add_mod_ref(&ctx, mod, &m->deviated_by) != LY_EEXIST) ||
-                    !lys_precompile_mod_set_is_all_implemented(&set)) {
-                LY_CHECK_GOTO(ret = ly_set_merge(&mod_set, &set, 0, NULL), cleanup);
-            }
-            ly_set_erase(&set, NULL);
+        /* add this module into the target module augmented_by, if not there and implemented */
+        if ((lys_array_add_mod_ref(&ctx, pmod->mod, &m->augmented_by) != LY_EEXIST) ||
+                !lys_precompile_mod_set_is_all_implemented(&set)) {
+            LY_CHECK_GOTO(ret = ly_set_merge(mod_set, &set, 0, NULL), cleanup);
         }
+        ly_set_erase(&set, NULL);
+    }
+
+cleanup:
+    ly_set_erase(&set, NULL);
+    return ret;
+}
+
+LY_ERR
+lys_precompile_augments_deviations(struct lys_module *mod, struct lys_glob_unres *unres)
+{
+    LY_ERR ret = LY_SUCCESS, r;
+    LY_ARRAY_COUNT_TYPE u;
+    struct lys_module *m;
+    struct lysp_module *submod;
+    const char **imp_f, *all_f[] = {"*", NULL};
+    uint32_t i;
+    struct ly_set mod_set = {0};
+
+    /* module */
+    LY_CHECK_GOTO(ret = lys_precompile_mod_augments_deviations(mod->parsed, &mod_set), cleanup);
+
+    /* submodules */
+    LY_ARRAY_FOR(mod->parsed->includes, u) {
+        submod = (struct lysp_module *)mod->parsed->includes[u].submodule;
+        LY_CHECK_GOTO(ret = lys_precompile_mod_augments_deviations(submod, &mod_set), cleanup);
     }
 
     for (i = 0; i < mod_set.count; ++i) {
@@ -2365,7 +2486,6 @@
     }
 
 cleanup:
-    ly_set_erase(&set, NULL);
     ly_set_erase(&mod_set, NULL);
     return ret;
 }
@@ -2417,73 +2537,3 @@
         }
     }
 }
-
-LIBYANG_API_DEF LY_ERR
-lys_compile_extension_instance_find_augment_target(struct lysc_ctx *ctx, const char *path,
-        struct lysc_ext_instance **aug_ext, struct lysc_node **aug_target)
-{
-    LY_ERR rc;
-    LY_ARRAY_COUNT_TYPE u;
-    struct lys_module *target_mod;
-    struct lysc_ext_instance *target_ext = NULL, *prev_ext;
-    const struct lysc_node *target;
-    const char *id, *id2, *name;
-    size_t name_len;
-    uint16_t flag;
-
-    LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, ctx, path, aug_target, LY_EINVAL);
-
-    /* skip first slash */
-    id = path;
-    if (id[0] != '/') {
-        LOGVAL(ctx->ctx, LYVE_SYNTAX, "Invalid absolute-schema-nodeid \"%s\".", path);
-        return LY_EVALID;
-    }
-    ++id;
-
-    /* find the next slash */
-    id2 = strchr(id, '/');
-    if (!id2) {
-        LOGVAL(ctx->ctx, LYVE_SYNTAX, "Invalid absolute-schema-nodeid \"%s\".", path);
-        return LY_EVALID;
-    }
-
-    /* find the target module */
-    target_mod = (struct lys_module *)lys_schema_node_get_module(ctx->ctx, id, id2 - id, ctx->pmod, &name, &name_len);
-    if (!target_mod) {
-        return LY_ENOTFOUND;
-    } else if (!target_mod->implemented) {
-        LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Augment target extension instance \"%*.s\" in a non-implemented module \"%s\".",
-                (int)name_len, name, target_mod->name);
-        return LY_ENOTFOUND;
-    }
-
-    /* find the extension instance */
-    LY_ARRAY_FOR(target_mod->compiled->exts, u) {
-        if (!ly_strncmp(target_mod->compiled->exts[u].argument, name, name_len)) {
-            target_ext = &target_mod->compiled->exts[u];
-            break;
-        }
-    }
-    if (!target_ext) {
-        LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Augment target extension instance \"%*.s\" not found in module \"%s\".",
-                (int)name_len, name, target_mod->name);
-        return LY_ENOTFOUND;
-    }
-
-    /* find the augment target with the correct compile context */
-    prev_ext = ctx->ext;
-    ctx->ext = target_ext;
-    rc = lysc_resolve_schema_nodeid(ctx, id2, strlen(id2), NULL, LY_VALUE_SCHEMA, ctx->pmod, 0, &target, &flag);
-    ctx->ext = prev_ext;
-    LY_CHECK_RET(rc);
-
-    /* add this module into the target module augmented_by, if not there */
-    lys_array_add_mod_ref(ctx, ctx->cur_mod, &target_mod->augmented_by);
-
-    if (aug_ext) {
-        *aug_ext = target_ext;
-    }
-    *aug_target = (struct lysc_node *)target;
-    return LY_SUCCESS;
-}