summaryrefslogtreecommitdiffstats
path: root/src/plugins_exts.c
blob: 71449ba7b9a72002bf98f26ce04d434ab36da467 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
/**
 * @file plugins_exts.c
 * @author Radek Krejci <rkrejci@cesnet.cz>
 * @author Michal Vasko <mvasko@cesnet.cz>
 * @brief helper functions for extension plugins
 *
 * Copyright (c) 2019 - 2024 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
 */

#include "plugins_exts.h"

#include <assert.h>
#include <stdint.h>
#include <stdlib.h>

#include "dict.h"
#include "ly_common.h"
#include "parser_internal.h"
#include "printer_internal.h"
#include "schema_compile.h"
#include "schema_compile_amend.h"
#include "schema_compile_node.h"
#include "schema_features.h"
#include "tree_schema_internal.h"

LIBYANG_API_DEF const struct lysp_module *
lyplg_ext_parse_get_cur_pmod(const struct lysp_ctx *pctx)
{
    return PARSER_CUR_PMOD(pctx);
}

LIBYANG_API_DEF LY_ERR
lyplg_ext_parse_extension_instance(struct lysp_ctx *pctx, struct lysp_ext_instance *ext)
{
    LY_ERR rc = LY_SUCCESS;
    LY_ARRAY_COUNT_TYPE u;
    struct lysp_stmt *stmt;

    /* check for invalid substatements */
    LY_LIST_FOR(ext->child, stmt) {
        if (stmt->flags & (LYS_YIN_ATTR | LYS_YIN_ARGUMENT)) {
            continue;
        }
        LY_ARRAY_FOR(ext->substmts, u) {
            if (ext->substmts[u].stmt == stmt->kw) {
                break;
            }
        }
        if (u == LY_ARRAY_COUNT(ext->substmts)) {
            LOGVAL(PARSER_CTX(pctx), LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s%s%s\" extension instance.",
                    stmt->stmt, ext->name, ext->argument ? " " : "", ext->argument ? ext->argument : "");
            rc = LY_EVALID;
            goto cleanup;
        }
    }

    /* parse all the known statements */
    LY_ARRAY_FOR(ext->substmts, u) {
        LY_LIST_FOR(ext->child, stmt) {
            if (ext->substmts[u].stmt != stmt->kw) {
                continue;
            }

            if ((rc = lys_parse_ext_instance_stmt(pctx, &ext->substmts[u], stmt))) {
                goto cleanup;
            }
        }
    }

cleanup:
    return rc;
}

/**
 * @brief Compile an instance extension statement.
 *
 * @param[in] ctx Compile context.
 * @param[in] parsed_p Parsed ext instance substatement structure.
 * @param[in] ext Compiled ext instance.
 * @param[in] substmt Compled ext instance substatement info.
 * @return LY_ERR value.
 */
static LY_ERR
lys_compile_ext_instance_stmt(struct lysc_ctx *ctx, void **parsed_p, struct lysc_ext_instance *ext,
        struct lysc_ext_substmt *substmt)
{
    LY_ERR rc = LY_SUCCESS;
    ly_bool length_restr = 0;
    LY_DATA_TYPE basetype;

    assert(*parsed_p);

    /* compilation wthout any storage */
    if (substmt->stmt == LY_STMT_IF_FEATURE) {
        ly_bool enabled;

        /* evaluate */
        LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, *parsed_p, &enabled), cleanup);
        if (!enabled) {
            /* it is disabled, remove the whole extension instance */
            rc = LY_ENOT;
        }
    }

    if (!substmt->storage_p) {
        /* nothing to store */
        goto cleanup;
    }

    switch (substmt->stmt) {
    case LY_STMT_NOTIFICATION:
    case LY_STMT_INPUT:
    case LY_STMT_OUTPUT:
    case LY_STMT_ACTION:
    case LY_STMT_RPC:
    case LY_STMT_ANYDATA:
    case LY_STMT_ANYXML:
    case LY_STMT_CASE:
    case LY_STMT_CHOICE:
    case LY_STMT_CONTAINER:
    case LY_STMT_LEAF:
    case LY_STMT_LEAF_LIST:
    case LY_STMT_LIST:
    case LY_STMT_USES: {
        const uint16_t flags;
        struct lysp_node *pnodes, *pnode;
        struct lysc_node *node;

        lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags);
        pnodes = *parsed_p;

        /* compile nodes */
        LY_LIST_FOR(pnodes, pnode) {
            if (pnode->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
                /* manual compile */
                node = calloc(1, sizeof(struct lysc_node_action_inout));
                LY_CHECK_ERR_GOTO(!node, LOGMEM(ctx->ctx); rc = LY_EMEM, cleanup);
                LY_CHECK_GOTO(rc = lys_compile_node_action_inout(ctx, pnode, node), cleanup);
                LY_CHECK_GOTO(rc = lys_compile_node_connect(ctx, NULL, node), cleanup);
            } else {
                /* ctx->ext substatement storage is used as the document root */
                LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, NULL, flags, NULL), cleanup);
            }
        }
        break;
    }
    case LY_STMT_ARGUMENT:
    case LY_STMT_CONTACT:
    case LY_STMT_DESCRIPTION:
    case LY_STMT_ERROR_APP_TAG:
    case LY_STMT_ERROR_MESSAGE:
    case LY_STMT_KEY:
    case LY_STMT_MODIFIER:
    case LY_STMT_NAMESPACE:
    case LY_STMT_ORGANIZATION:
    case LY_STMT_PRESENCE:
    case LY_STMT_REFERENCE:
    case LY_STMT_UNITS:
        /* just make a copy */
        LY_CHECK_GOTO(rc = lydict_insert(ctx->ctx, *parsed_p, 0, (const char **)substmt->storage_p), cleanup);
        break;

    case LY_STMT_BIT:
        basetype = LY_TYPE_BITS;
    /* fallthrough */
    case LY_STMT_ENUM:
        if (substmt->stmt == LY_STMT_ENUM) {
            basetype = LY_TYPE_ENUM;
        }

        /* compile */
        rc = lys_compile_type_enums(ctx, *parsed_p, basetype, NULL, (struct lysc_type_bitenum_item **)substmt->storage_p);
        LY_CHECK_GOTO(rc, cleanup);
        break;

    case LY_STMT_CONFIG: {
        uint16_t flags;

        if (!(ctx->compile_opts & LYS_COMPILE_NO_CONFIG)) {
            memcpy(&flags, parsed_p, 2);
            if (flags & LYS_CONFIG_MASK) {
                /* explicitly set */
                flags |= LYS_SET_CONFIG;
            } else if (ext->parent_stmt & LY_STMT_DATA_NODE_MASK) {
                /* inherit */
                flags = ((struct lysc_node *)ext->parent)->flags & LYS_CONFIG_MASK;
            } else {
                /* default config */
                flags = LYS_CONFIG_W;
            }
            memcpy(substmt->storage_p, &flags, 2);
        } /* else leave zero */
        break;
    }
    case LY_STMT_MUST: {
        const struct lysp_restr *restrs = *parsed_p;

        /* sized array */
        COMPILE_ARRAY_GOTO(ctx, restrs, *(struct lysc_must **)substmt->storage_p, lys_compile_must, rc, cleanup);
        break;
    }
    case LY_STMT_WHEN: {
        const uint16_t flags;
        const struct lysp_when *when = *parsed_p;

        /* read compiled status */
        lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags);

        /* compile */
        LY_CHECK_GOTO(rc = lys_compile_when(ctx, when, flags, NULL, NULL, NULL, (struct lysc_when **)substmt->storage_p), cleanup);
        break;
    }
    case LY_STMT_FRACTION_DIGITS:
    case LY_STMT_REQUIRE_INSTANCE:
        /* just make a copy */
        memcpy(substmt->storage_p, parsed_p, 1);
        break;

    case LY_STMT_MANDATORY:
    case LY_STMT_ORDERED_BY:
    case LY_STMT_STATUS:
        /* just make a copy */
        memcpy(substmt->storage_p, parsed_p, 2);
        break;

    case LY_STMT_MAX_ELEMENTS:
    case LY_STMT_MIN_ELEMENTS:
        /* just make a copy */
        memcpy(substmt->storage_p, parsed_p, 4);
        break;

    case LY_STMT_POSITION:
    case LY_STMT_VALUE:
        /* just make a copy */
        memcpy(substmt->storage_p, parsed_p, 8);
        break;

    case LY_STMT_IDENTITY:
        /* compile */
        rc = lys_identity_precompile(ctx, NULL, NULL, *parsed_p, (struct lysc_ident **)substmt->storage_p);
        LY_CHECK_GOTO(rc, cleanup);
        break;

    case LY_STMT_LENGTH:
        length_restr = 1;
    /* fallthrough */
    case LY_STMT_RANGE:
        /* compile, use uint64 default range */
        rc = lys_compile_type_range(ctx, *parsed_p, LY_TYPE_UINT64, length_restr, 0, NULL, (struct lysc_range **)substmt->storage_p);
        LY_CHECK_GOTO(rc, cleanup);
        break;

    case LY_STMT_PATTERN:
        /* compile */
        rc = lys_compile_type_patterns(ctx, *parsed_p, NULL, (struct lysc_pattern ***)substmt->storage_p);
        LY_CHECK_GOTO(rc, cleanup);
        break;

    case LY_STMT_TYPE: {
        const uint16_t flags;
        const char *units;
        const struct lysp_type *ptype = *parsed_p;

        /* read compiled info */
        lyplg_ext_get_storage(ext, LY_STMT_STATUS, sizeof flags, (const void **)&flags);
        lyplg_ext_get_storage(ext, LY_STMT_UNITS, sizeof units, (const void **)&units);

        /* compile */
        rc = lys_compile_type(ctx, NULL, flags, ext->def->name, ptype, (struct lysc_type **)substmt->storage_p, &units, NULL);
        LY_CHECK_GOTO(rc, cleanup);
        LY_ATOMIC_INC_BARRIER((*(struct lysc_type **)substmt->storage_p)->refcount);
        break;
    }
    case LY_STMT_EXTENSION_INSTANCE: {
        struct lysp_ext_instance *extps = *parsed_p;

        /* compile sized array */
        COMPILE_EXTS_GOTO(ctx, extps, *(struct lysc_ext_instance **)substmt->storage_p, ext, rc, cleanup);
        break;
    }
    case LY_STMT_AUGMENT:
    case LY_STMT_GROUPING:
    case LY_STMT_BASE:
    case LY_STMT_BELONGS_TO:
    case LY_STMT_DEFAULT:
    case LY_STMT_DEVIATE:
    case LY_STMT_DEVIATION:
    case LY_STMT_EXTENSION:
    case LY_STMT_FEATURE:
    case LY_STMT_IF_FEATURE:
    case LY_STMT_IMPORT:
    case LY_STMT_INCLUDE:
    case LY_STMT_MODULE:
    case LY_STMT_PATH:
    case LY_STMT_PREFIX:
    case LY_STMT_REFINE:
    case LY_STMT_REVISION:
    case LY_STMT_REVISION_DATE:
    case LY_STMT_SUBMODULE:
    case LY_STMT_TYPEDEF:
    case LY_STMT_UNIQUE:
    case LY_STMT_YANG_VERSION:
    case LY_STMT_YIN_ELEMENT:
        LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Statement \"%s\" compilation is not supported.", lyplg_ext_stmt2str(substmt->stmt));
        rc = LY_EVALID;
        goto cleanup;

    default:
        LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Statement \"%s\" is not supported as an extension "
                "(found in \"%s%s%s\") substatement.", lyplg_ext_stmt2str(substmt->stmt), ext->def->name,
                ext->argument ? " " : "", ext->argument ? ext->argument : "");
        rc = LY_EVALID;
        goto cleanup;
    }

cleanup:
    return rc;
}

LIBYANG_API_DEF LY_ERR
lyplg_ext_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext_instance *extp,
        struct lysc_ext_instance *ext)
{
    LY_ERR rc = LY_SUCCESS;
    LY_ARRAY_COUNT_TYPE u, v;
    enum ly_stmt stmtp;
    void **storagep;
    struct ly_set storagep_compiled = {0};

    LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, ctx, extp, ext, LY_EINVAL);

    /* note into the compile context that we are processing extension now */
    ctx->ext = ext;

    LY_ARRAY_FOR(extp->substmts, u) {
        stmtp = extp->substmts[u].stmt;
        storagep = extp->substmts[u].storage_p;

        if (!*storagep || ly_set_contains(&storagep_compiled, storagep, NULL)) {
            /* nothing parsed or already compiled (for example, if it is a linked list of parsed nodes) */
            continue;
        }

        LY_ARRAY_FOR(ext->substmts, v) {
            if (stmtp != ext->substmts[v].stmt) {
                continue;
            }

            if ((rc = lys_compile_ext_instance_stmt(ctx, storagep, ext, &ext->substmts[v]))) {
                goto cleanup;
            }

            /* parsed substatement compiled */
            break;
        }

        /* compiled */
        ly_set_add(&storagep_compiled, storagep, 1, NULL);
    }

cleanup:
    ctx->ext = NULL;
    ly_set_erase(&storagep_compiled, NULL);
    return rc;
}

LIBYANG_API_DEF struct ly_ctx *
lyplg_ext_compile_get_ctx(const struct lysc_ctx *ctx)
{
    return ctx->ctx;
}

LIBYANG_API_DEF uint32_t *
lyplg_ext_compile_get_options(const struct lysc_ctx *ctx)
{
    return &((struct lysc_ctx *)ctx)->compile_opts;
}

LIBYANG_API_DEF const struct lys_module *
lyplg_ext_compile_get_cur_mod(const struct lysc_ctx *ctx)
{
    return ctx->cur_mod;
}

LIBYANG_API_DEF struct lysp_module *
lyplg_ext_compile_get_pmod(const struct lysc_ctx *ctx)
{
    return ctx->pmod;
}

LIBYANG_API_DEF struct ly_out **
lyplg_ext_print_get_out(const struct lyspr_ctx *ctx)
{
    return &((struct lyspr_ctx *)ctx)->out;
}

LIBYANG_API_DEF uint32_t *
lyplg_ext_print_get_options(const struct lyspr_ctx *ctx)
{
    return &((struct lyspr_ctx *)ctx)->options;
}

LIBYANG_API_DEF uint16_t *
lyplg_ext_print_get_level(const struct lyspr_ctx *ctx)
{
    return &((struct lyspr_ctx *)ctx)->level;
}

LIBYANG_API_DECL LY_ERR
lyplg_ext_sprinter_ctree_add_ext_nodes(const struct lyspr_tree_ctx *ctx, struct lysc_ext_instance *ext,
        lyplg_ext_sprinter_ctree_override_clb clb)
{
    LY_ERR rc = LY_SUCCESS;
    uint32_t i;
    struct lysc_node *schema;

    LY_CHECK_ARG_RET(NULL, ctx, ext, LY_EINVAL);

    LY_ARRAY_FOR(ext->substmts, i) {
        switch (ext->substmts[i].stmt) {
        case LY_STMT_NOTIFICATION:
        case LY_STMT_INPUT:
        case LY_STMT_OUTPUT:
        case LY_STMT_ACTION:
        case LY_STMT_RPC:
        case LY_STMT_ANYDATA:
        case LY_STMT_ANYXML:
        case LY_STMT_CASE:
        case LY_STMT_CHOICE:
        case LY_STMT_CONTAINER:
        case LY_STMT_LEAF:
        case LY_STMT_LEAF_LIST:
        case LY_STMT_LIST:
            schema = *ext->substmts[i].storage_p;
            if (schema) {
                rc = lyplg_ext_sprinter_ctree_add_nodes(ctx, schema, clb);
                return rc;
            }
        default:
            break;
        }
    }

    return rc;
}

LIBYANG_API_DECL LY_ERR
lyplg_ext_sprinter_ptree_add_ext_nodes(const struct lyspr_tree_ctx *ctx, struct lysp_ext_instance *ext,
        lyplg_ext_sprinter_ptree_override_clb clb)
{
    LY_ERR rc = LY_SUCCESS;
    uint32_t i;
    struct lysp_node *schema;

    LY_CHECK_ARG_RET(NULL, ctx, ext, LY_EINVAL);

    LY_ARRAY_FOR(ext->substmts, i) {
        switch (ext->substmts[i].stmt) {
        case LY_STMT_NOTIFICATION:
        case LY_STMT_INPUT:
        case LY_STMT_OUTPUT:
        case LY_STMT_ACTION:
        case LY_STMT_RPC:
        case LY_STMT_ANYDATA:
        case LY_STMT_ANYXML:
        case LY_STMT_CASE:
        case LY_STMT_CHOICE:
        case LY_STMT_CONTAINER:
        case LY_STMT_LEAF:
        case LY_STMT_LEAF_LIST:
        case LY_STMT_LIST:
            schema = *ext->substmts[i].storage_p;
            if (schema) {
                rc = lyplg_ext_sprinter_ptree_add_nodes(ctx, schema, clb);
                return rc;
            }
        default:
            break;
        }
    }

    return rc;
}

LIBYANG_API_DECL LY_ERR
lyplg_ext_sprinter_ctree_add_nodes(const struct lyspr_tree_ctx *ctx, struct lysc_node *nodes,
        lyplg_ext_sprinter_ctree_override_clb clb)
{
    struct lyspr_tree_schema *new;

    LY_CHECK_ARG_RET(NULL, ctx, LY_EINVAL);

    if (!nodes) {
        return LY_SUCCESS;
    }

    LY_ARRAY_NEW_RET(NULL, ((struct lyspr_tree_ctx *)ctx)->schemas, new, LY_EMEM);
    new->compiled = 1;
    new->ctree = nodes;
    new->cn_overr = clb;

    return LY_SUCCESS;
}

LIBYANG_API_DECL LY_ERR
lyplg_ext_sprinter_ptree_add_nodes(const struct lyspr_tree_ctx *ctx, struct lysp_node *nodes,
        lyplg_ext_sprinter_ptree_override_clb clb)
{
    struct lyspr_tree_schema *new;

    LY_CHECK_ARG_RET(NULL, ctx, LY_EINVAL);

    if (!nodes) {
        return LY_SUCCESS;
    }

    LY_ARRAY_NEW_RET(NULL, ((struct lyspr_tree_ctx *)ctx)->schemas, new, LY_EMEM);
    new->compiled = 0;
    new->ptree = nodes;
    new->pn_overr = clb;

    return LY_SUCCESS;
}

LIBYANG_API_DECL LY_ERR
lyplg_ext_sprinter_tree_set_priv(const struct lyspr_tree_ctx *ctx, void *plugin_priv, void (*free_clb)(void *plugin_priv))
{
    LY_CHECK_ARG_RET(NULL, ctx, LY_EINVAL);

    ((struct lyspr_tree_ctx *)ctx)->plugin_priv = plugin_priv;
    ((struct lyspr_tree_ctx *)ctx)->free_plugin_priv = free_clb;

    return LY_SUCCESS;
}

LIBYANG_API_DEF const char *
lyplg_ext_stmt2str(enum ly_stmt stmt)
{
    if (stmt == LY_STMT_EXTENSION_INSTANCE) {
        return "extension instance";
    } else {
        return lys_stmt_str(stmt);
    }
}

LIBYANG_API_DEF enum ly_stmt
lyplg_ext_nodetype2stmt(uint16_t nodetype)
{
    switch (nodetype) {
    case LYS_CONTAINER:
        return LY_STMT_CONTAINER;
    case LYS_CHOICE:
        return LY_STMT_CHOICE;
    case LYS_LEAF:
        return LY_STMT_LEAF;
    case LYS_LEAFLIST:
        return LY_STMT_LEAF_LIST;
    case LYS_LIST:
        return LY_STMT_LIST;
    case LYS_ANYXML:
        return LY_STMT_ANYXML;
    case LYS_ANYDATA:
        return LY_STMT_ANYDATA;
    case LYS_CASE:
        return LY_STMT_CASE;
    case LYS_RPC:
        return LY_STMT_RPC;
    case LYS_ACTION:
        return LY_STMT_ACTION;
    case LYS_NOTIF:
        return LY_STMT_NOTIFICATION;
    case LYS_USES:
        return LY_STMT_USES;
    case LYS_INPUT:
        return LY_STMT_INPUT;
    case LYS_OUTPUT:
        return LY_STMT_OUTPUT;
    default:
        return LY_STMT_NONE;
    }
}

LY_ERR
lyplg_ext_get_storage_p(const struct lysc_ext_instance *ext, int stmt, void ***storage_pp)
{
    LY_ARRAY_COUNT_TYPE u;
    enum ly_stmt match = 0;

    *storage_pp = NULL;

    if (!(stmt & LY_STMT_NODE_MASK)) {
        /* matching a non-node statement */
        match = stmt;
    }

    LY_ARRAY_FOR(ext->substmts, u) {
        if ((match && (ext->substmts[u].stmt == match)) || (!match && (ext->substmts[u].stmt & stmt))) {
            *storage_pp = ext->substmts[u].storage_p;
            return LY_SUCCESS;
        }
    }

    return LY_ENOT;
}

LIBYANG_API_DEF LY_ERR
lyplg_ext_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size, const void **storage)
{
    LY_ERR rc = LY_SUCCESS;
    void **s_p;

    /* get pointer to the storage, is set even on error */
    rc = lyplg_ext_get_storage_p(ext, stmt, &s_p);

    /* assign */
    if (s_p) {
        memcpy(storage, s_p, storage_size);
    } else {
        memset(storage, 0, storage_size);
    }

    return rc;
}

LIBYANG_API_DEF LY_ERR
lyplg_ext_parsed_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size, const void **storage)
{
    LY_ARRAY_COUNT_TYPE u;
    const struct lysp_ext_instance *extp = NULL;
    enum ly_stmt match = 0;
    void **s_p = NULL;

    LY_CHECK_ARG_RET(NULL, ext, ext->module->parsed, LY_EINVAL);

    /* find the parsed ext instance */
    LY_ARRAY_FOR(ext->module->parsed->exts, u) {
        extp = &ext->module->parsed->exts[u];

        if (ext->def == extp->def->compiled) {
            break;
        }
        extp = NULL;
    }
    assert(extp);

    if (!(stmt & LY_STMT_NODE_MASK)) {
        /* matching a non-node statement */
        match = stmt;
    }

    /* get the substatement */
    LY_ARRAY_FOR(extp->substmts, u) {
        if ((match && (extp->substmts[u].stmt == match)) || (!match && (extp->substmts[u].stmt & stmt))) {
            s_p = extp->substmts[u].storage_p;
            break;
        }
    }

    /* assign */
    if (s_p) {
        memcpy(storage, s_p, storage_size);
    } else {
        memset(storage, 0, storage_size);
    }

    return LY_SUCCESS;
}

LIBYANG_API_DEF LY_ERR
lyplg_ext_get_data(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, void **ext_data, ly_bool *ext_data_free)
{
    LY_ERR rc;

    if (!ctx->ext_clb) {
        lyplg_ext_compile_log(NULL, ext, LY_LLERR, LY_EINVAL, "Failed to get extension data, no callback set.");
        return LY_EINVAL;
    }

    if ((rc = ctx->ext_clb(ext, ctx->ext_clb_data, ext_data, ext_data_free))) {
        lyplg_ext_compile_log(NULL, ext, LY_LLERR, rc, "Callback for getting ext data failed.");
    }
    return rc;
}