Coverage Report

Created: 2025-12-05 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libyang/src/schema_features.c
Line
Count
Source
1
/**
2
 * @file schema_features.c
3
 * @author Radek Krejci <rkrejci@cesnet.cz>
4
 * @brief Schema feature handling
5
 *
6
 * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
7
 *
8
 * This source code is licensed under BSD 3-Clause License (the "License").
9
 * You may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     https://opensource.org/licenses/BSD-3-Clause
13
 */
14
15
#define _GNU_SOURCE
16
17
#include "schema_features.h"
18
19
#include <assert.h>
20
#include <ctype.h>
21
#include <stdint.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "common.h"
27
#include "log.h"
28
#include "set.h"
29
#include "tree.h"
30
#include "tree_edit.h"
31
#include "tree_schema.h"
32
#include "tree_schema_internal.h"
33
34
0
#define IFF_RECORDS_IN_BYTE 4
35
0
#define IFF_RECORD_BITS 2
36
0
#define IFF_RECORD_MASK 0x3
37
38
uint8_t
39
lysc_iff_getop(uint8_t *list, size_t pos)
40
0
{
41
0
    uint8_t *item;
42
0
    uint8_t mask = IFF_RECORD_MASK, result;
43
44
0
    item = &list[pos / IFF_RECORDS_IN_BYTE];
45
0
    result = (*item) & (mask << IFF_RECORD_BITS * (pos % IFF_RECORDS_IN_BYTE));
46
0
    return result >> IFF_RECORD_BITS * (pos % IFF_RECORDS_IN_BYTE);
47
0
}
48
49
static LY_ERR
50
lysc_iffeature_value_(const struct lysc_iffeature *iff, size_t *index_e, size_t *index_f)
51
0
{
52
0
    uint8_t op;
53
0
    LY_ERR a, b;
54
55
0
    op = lysc_iff_getop(iff->expr, *index_e);
56
0
    (*index_e)++;
57
58
0
    switch (op) {
59
0
    case LYS_IFF_F:
60
        /* resolve feature */
61
0
        return (iff->features[(*index_f)++]->flags & LYS_FENABLED) ? LY_SUCCESS : LY_ENOT;
62
0
    case LYS_IFF_NOT:
63
        /* invert result */
64
0
        return lysc_iffeature_value_(iff, index_e, index_f) == LY_SUCCESS ? LY_ENOT : LY_SUCCESS;
65
0
    case LYS_IFF_AND:
66
0
    case LYS_IFF_OR:
67
0
        a = lysc_iffeature_value_(iff, index_e, index_f);
68
0
        b = lysc_iffeature_value_(iff, index_e, index_f);
69
0
        if (op == LYS_IFF_AND) {
70
0
            if ((a == LY_SUCCESS) && (b == LY_SUCCESS)) {
71
0
                return LY_SUCCESS;
72
0
            } else {
73
0
                return LY_ENOT;
74
0
            }
75
0
        } else { /* LYS_IFF_OR */
76
0
            if ((a == LY_SUCCESS) || (b == LY_SUCCESS)) {
77
0
                return LY_SUCCESS;
78
0
            } else {
79
0
                return LY_ENOT;
80
0
            }
81
0
        }
82
0
    }
83
84
0
    return LY_ENOT;
85
0
}
86
87
API LY_ERR
88
lysc_iffeature_value(const struct lysc_iffeature *iff)
89
0
{
90
0
    size_t index_e = 0, index_f = 0;
91
92
0
    LY_CHECK_ARG_RET(NULL, iff, LY_EINVAL);
93
94
0
    if (iff->expr) {
95
0
        return lysc_iffeature_value_(iff, &index_e, &index_f);
96
0
    }
97
0
    return LY_ENOT;
98
0
}
99
100
API struct lysp_feature *
101
lysp_feature_next(const struct lysp_feature *last, const struct lysp_module *pmod, uint32_t *idx)
102
0
{
103
0
    struct lysp_feature *features;
104
105
0
    if (!*idx) {
106
        /* module features */
107
0
        features = pmod->features;
108
0
    } else if ((*idx - 1) < LY_ARRAY_COUNT(pmod->includes)) {
109
        /* submodule features */
110
0
        features = pmod->includes[*idx - 1].submodule->features;
111
0
    } else {
112
        /* no more features */
113
0
        return NULL;
114
0
    }
115
116
    /* get the next feature */
117
0
    if (features && (!last || (&features[LY_ARRAY_COUNT(features) - 1] != last))) {
118
0
        return !last ? &features[0] : (struct lysp_feature *)last + 1;
119
0
    }
120
121
    /* no more features in current (sub)module */
122
0
    ++(*idx);
123
0
    return lysp_feature_next(NULL, pmod, idx);
124
0
}
125
126
/**
127
 * @brief Find a feature of the given name and referenced in the given module.
128
 *
129
 * @param[in] pmod Module where the feature was referenced (used to resolve prefix of the feature).
130
 * @param[in] name Name of the feature including possible prefix.
131
 * @param[in] len Length of the string representing the feature identifier in the name variable (mandatory!).
132
 * @param[in] prefixed Whether the feature name can be prefixed.
133
 * @return Pointer to the feature structure if found, NULL otherwise.
134
 */
135
static struct lysp_feature *
136
lysp_feature_find(const struct lysp_module *pmod, const char *name, size_t len, ly_bool prefixed)
137
0
{
138
0
    const struct lys_module *mod;
139
0
    const char *ptr;
140
0
    struct lysp_feature *f = NULL;
141
0
    uint32_t idx = 0;
142
143
0
    assert(pmod);
144
145
0
    if (prefixed && (ptr = ly_strnchr(name, ':', len))) {
146
        /* we have a prefixed feature */
147
0
        mod = ly_resolve_prefix(pmod->mod->ctx, name, ptr - name, LY_VALUE_SCHEMA, (void *)pmod);
148
0
        LY_CHECK_RET(!mod, NULL);
149
150
0
        pmod = mod->parsed;
151
0
        len = len - (ptr - name) - 1;
152
0
        name = ptr + 1;
153
0
    }
154
155
    /* feature without prefix, look in main module and all submodules */
156
0
    if (pmod->is_submod) {
157
0
        pmod = pmod->mod->parsed;
158
0
    }
159
160
    /* we have the correct module, get the feature */
161
0
    while ((f = lysp_feature_next(f, pmod, &idx))) {
162
0
        if (!ly_strncmp(f->name, name, len)) {
163
0
            return f;
164
0
        }
165
0
    }
166
167
0
    return NULL;
168
0
}
169
170
API LY_ERR
171
lys_feature_value(const struct lys_module *module, const char *feature)
172
0
{
173
0
    const struct lysp_feature *f;
174
175
0
    LY_CHECK_ARG_RET(NULL, module, module->parsed, feature, LY_EINVAL);
176
177
    /* search for the specified feature */
178
0
    f = lysp_feature_find(module->parsed, feature, strlen(feature), 0);
179
0
    LY_CHECK_RET(!f, LY_ENOTFOUND);
180
181
    /* feature disabled */
182
0
    if (!(f->flags & LYS_FENABLED)) {
183
0
        return LY_ENOT;
184
0
    }
185
186
    /* feature enabled */
187
0
    return LY_SUCCESS;
188
0
}
189
190
/**
191
 * @brief Stack for processing if-feature expressions.
192
 */
193
struct iff_stack {
194
    size_t size;    /**< number of items in the stack */
195
    size_t index;   /**< first empty item */
196
    uint8_t *stack; /**< stack - array of @ref ifftokens to create the if-feature expression in prefix format */
197
};
198
0
#define IFF_STACK_SIZE_STEP 4
199
200
/**
201
 * @brief Add @ref ifftokens into the stack.
202
 * @param[in] stack The if-feature stack to use.
203
 * @param[in] value One of the @ref ifftokens to store in the stack.
204
 * @return LY_EMEM in case of memory allocation error
205
 * @return LY_ESUCCESS if the value successfully stored.
206
 */
207
static LY_ERR
208
iff_stack_push(struct iff_stack *stack, uint8_t value)
209
0
{
210
0
    if (stack->index == stack->size) {
211
0
        stack->size += IFF_STACK_SIZE_STEP;
212
0
        stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
213
0
        LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
214
0
    }
215
0
    stack->stack[stack->index++] = value;
216
0
    return LY_SUCCESS;
217
0
}
218
219
/**
220
 * @brief Get (and remove) the last item form the stack.
221
 * @param[in] stack The if-feature stack to use.
222
 * @return The value from the top of the stack.
223
 */
224
static uint8_t
225
iff_stack_pop(struct iff_stack *stack)
226
0
{
227
0
    assert(stack && stack->index);
228
229
0
    stack->index--;
230
0
    return stack->stack[stack->index];
231
0
}
232
233
/**
234
 * @brief Clean up the stack.
235
 * @param[in] stack The if-feature stack to use.
236
 */
237
static void
238
iff_stack_clean(struct iff_stack *stack)
239
0
{
240
0
    stack->size = 0;
241
0
    free(stack->stack);
242
0
}
243
244
/**
245
 * @brief Store the @ref ifftokens (@p op) on the given position in the 2bits array
246
 * (libyang format of the if-feature expression).
247
 * @param[in,out] list The 2bits array to modify.
248
 * @param[in] op The operand (@ref ifftokens) to store.
249
 * @param[in] pos Position (0-based) where to store the given @p op.
250
 */
251
static void
252
iff_setop(uint8_t *list, uint8_t op, size_t pos)
253
0
{
254
0
    uint8_t *item;
255
0
    uint8_t mask = IFF_RECORD_MASK;
256
257
0
    assert(op <= IFF_RECORD_MASK); /* max 2 bits */
258
259
0
    item = &list[pos / IFF_RECORDS_IN_BYTE];
260
0
    mask = mask << IFF_RECORD_BITS * (pos % IFF_RECORDS_IN_BYTE);
261
0
    *item = (*item) & ~mask;
262
0
    *item = (*item) | (op << IFF_RECORD_BITS * (pos % IFF_RECORDS_IN_BYTE));
263
0
}
264
265
#define LYS_IFF_LP 0x04 /**< Additional, temporary, value of @ref ifftokens: ( */
266
0
#define LYS_IFF_RP 0x08 /**< Additional, temporary, value of @ref ifftokens: ) */
267
268
static LY_ERR
269
lys_compile_iffeature(const struct ly_ctx *ctx, struct lysp_qname *qname, struct lysc_iffeature *iff)
270
0
{
271
0
    LY_ERR rc = LY_SUCCESS;
272
0
    const char *c = qname->str;
273
0
    int64_t i, j;
274
0
    int8_t op_len, last_not = 0, checkversion = 0;
275
0
    LY_ARRAY_COUNT_TYPE f_size = 0, expr_size = 0, f_exp = 1;
276
0
    uint8_t op;
277
0
    struct iff_stack stack = {0, 0, NULL};
278
0
    struct lysp_feature *f;
279
280
0
    assert(c);
281
282
    /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
283
0
    for (i = j = 0; c[i]; i++) {
284
0
        if (c[i] == '(') {
285
0
            j++;
286
0
            checkversion = 1;
287
0
            continue;
288
0
        } else if (c[i] == ')') {
289
0
            j--;
290
0
            continue;
291
0
        } else if (isspace(c[i])) {
292
0
            checkversion = 1;
293
0
            continue;
294
0
        }
295
296
0
        if (!strncmp(&c[i], "not", op_len = ly_strlen_const("not")) ||
297
0
                !strncmp(&c[i], "and", op_len = ly_strlen_const("and")) ||
298
0
                !strncmp(&c[i], "or", op_len = ly_strlen_const("or"))) {
299
0
            uint64_t spaces;
300
0
            for (spaces = 0; c[i + op_len + spaces] && isspace(c[i + op_len + spaces]); spaces++) {}
301
0
            if (c[i + op_len + spaces] == '\0') {
302
0
                LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - unexpected end of expression.", qname->str);
303
0
                return LY_EVALID;
304
0
            } else if (!isspace(c[i + op_len])) {
305
                /* feature name starting with the not/and/or */
306
0
                last_not = 0;
307
0
                f_size++;
308
0
            } else if (c[i] == 'n') { /* not operation */
309
0
                if (last_not) {
310
                    /* double not */
311
0
                    expr_size = expr_size - 2;
312
0
                    last_not = 0;
313
0
                } else {
314
0
                    last_not = 1;
315
0
                }
316
0
            } else { /* and, or */
317
0
                if (f_exp != f_size) {
318
0
                    LOGVAL(ctx, LYVE_SYNTAX_YANG,
319
0
                            "Invalid value \"%s\" of if-feature - missing feature/expression before \"%.*s\" operation.",
320
0
                            qname->str, op_len, &c[i]);
321
0
                    return LY_EVALID;
322
0
                }
323
0
                f_exp++;
324
325
                /* not a not operation */
326
0
                last_not = 0;
327
0
            }
328
0
            i += op_len;
329
0
        } else {
330
0
            f_size++;
331
0
            last_not = 0;
332
0
        }
333
0
        expr_size++;
334
335
0
        while (!isspace(c[i])) {
336
0
            if (!c[i] || (c[i] == ')') || (c[i] == '(')) {
337
0
                i--;
338
0
                break;
339
0
            }
340
0
            i++;
341
0
        }
342
0
    }
343
0
    if (j) {
344
        /* not matching count of ( and ) */
345
0
        LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.", qname->str);
346
0
        return LY_EVALID;
347
0
    }
348
0
    if (f_exp != f_size) {
349
        /* features do not match the needed arguments for the logical operations */
350
0
        LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - number of features in expression does not match "
351
0
                "the required number of operands for the operations.", qname->str);
352
0
        return LY_EVALID;
353
0
    }
354
355
0
    if (checkversion || (expr_size > 1)) {
356
        /* check that we have 1.1 module */
357
0
        if (qname->mod->version != LYS_VERSION_1_1) {
358
0
            LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", qname->str);
359
0
            return LY_EVALID;
360
0
        }
361
0
    }
362
363
    /* allocate the memory */
364
0
    LY_ARRAY_CREATE_RET(ctx, iff->features, f_size, LY_EMEM);
365
0
    iff->expr = calloc((j = (expr_size / IFF_RECORDS_IN_BYTE) + ((expr_size % IFF_RECORDS_IN_BYTE) ? 1 : 0)), sizeof *iff->expr);
366
0
    stack.stack = malloc(expr_size * sizeof *stack.stack);
367
0
    LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx); rc = LY_EMEM, error);
368
369
0
    stack.size = expr_size;
370
0
    f_size--; expr_size--; /* used as indexes from now */
371
372
0
    for (i--; i >= 0; i--) {
373
0
        if (c[i] == ')') {
374
            /* push it on stack */
375
0
            iff_stack_push(&stack, LYS_IFF_RP);
376
0
            continue;
377
0
        } else if (c[i] == '(') {
378
            /* pop from the stack into result all operators until ) */
379
0
            while ((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
380
0
                iff_setop(iff->expr, op, expr_size--);
381
0
            }
382
0
            continue;
383
0
        } else if (isspace(c[i])) {
384
0
            continue;
385
0
        }
386
387
        /* end of operator or operand -> find beginning and get what is it */
388
0
        j = i + 1;
389
0
        while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
390
0
            i--;
391
0
        }
392
0
        i++; /* go back by one step */
393
394
0
        if (!strncmp(&c[i], "not", ly_strlen_const("not")) && isspace(c[i + ly_strlen_const("not")])) {
395
0
            if (stack.index && (stack.stack[stack.index - 1] == LYS_IFF_NOT)) {
396
                /* double not */
397
0
                iff_stack_pop(&stack);
398
0
            } else {
399
                /* not has the highest priority, so do not pop from the stack
400
                 * as in case of AND and OR */
401
0
                iff_stack_push(&stack, LYS_IFF_NOT);
402
0
            }
403
0
        } else if (!strncmp(&c[i], "and", ly_strlen_const("and")) && isspace(c[i + ly_strlen_const("and")])) {
404
            /* as for OR - pop from the stack all operators with the same or higher
405
             * priority and store them to the result, then push the AND to the stack */
406
0
            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
407
0
                op = iff_stack_pop(&stack);
408
0
                iff_setop(iff->expr, op, expr_size--);
409
0
            }
410
0
            iff_stack_push(&stack, LYS_IFF_AND);
411
0
        } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
412
0
            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
413
0
                op = iff_stack_pop(&stack);
414
0
                iff_setop(iff->expr, op, expr_size--);
415
0
            }
416
0
            iff_stack_push(&stack, LYS_IFF_OR);
417
0
        } else {
418
            /* feature name, length is j - i */
419
420
            /* add it to the expression */
421
0
            iff_setop(iff->expr, LYS_IFF_F, expr_size--);
422
423
            /* now get the link to the feature definition */
424
0
            f = lysp_feature_find(qname->mod, &c[i], j - i, 1);
425
0
            if (!f) {
426
0
                LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".",
427
0
                        qname->str, (int)(j - i), &c[i]);
428
0
                rc = LY_EVALID;
429
0
                goto error;
430
0
            }
431
0
            iff->features[f_size] = f;
432
0
            LY_ARRAY_INCREMENT(iff->features);
433
0
            f_size--;
434
0
        }
435
0
    }
436
0
    while (stack.index) {
437
0
        op = iff_stack_pop(&stack);
438
0
        iff_setop(iff->expr, op, expr_size--);
439
0
    }
440
441
0
    if (++expr_size || ++f_size) {
442
        /* not all expected operators and operands found */
443
0
        LOGVAL(ctx, LYVE_SYNTAX_YANG, "Invalid value \"%s\" of if-feature - processing error.", qname->str);
444
0
        rc = LY_EINT;
445
0
    } else {
446
0
        rc = LY_SUCCESS;
447
0
    }
448
449
0
error:
450
    /* cleanup */
451
0
    iff_stack_clean(&stack);
452
453
0
    return rc;
454
0
}
455
456
LY_ERR
457
lys_eval_iffeatures(const struct ly_ctx *ctx, struct lysp_qname *iffeatures, ly_bool *enabled)
458
0
{
459
0
    LY_ERR ret;
460
0
    struct lysc_iffeature iff = {0};
461
462
0
    if (!iffeatures) {
463
0
        *enabled = 1;
464
0
        return LY_SUCCESS;
465
0
    }
466
467
0
    LY_CHECK_RET(lys_compile_iffeature(ctx, iffeatures, &iff));
468
469
0
    ret = lysc_iffeature_value(&iff);
470
0
    lysc_iffeature_free((struct ly_ctx *)ctx, &iff);
471
0
    if (ret == LY_ENOT) {
472
0
        *enabled = 0;
473
0
    } else if (ret) {
474
0
        return ret;
475
0
    } else {
476
0
        *enabled = 1;
477
0
    }
478
479
0
    return LY_SUCCESS;
480
0
}
481
482
/**
483
 * @brief Check whether all enabled features have their if-features satisfied.
484
 * Enabled features with false if-features are disabled with a warning.
485
 *
486
 * @param[in] pmod Parsed module features to check.
487
 * @return LY_ERR value.
488
 */
489
static LY_ERR
490
lys_check_features(struct lysp_module *pmod)
491
0
{
492
0
    LY_ERR r;
493
0
    uint32_t i = 0;
494
0
    struct lysp_feature *f = NULL;
495
496
0
    while ((f = lysp_feature_next(f, pmod, &i))) {
497
0
        if (!(f->flags & LYS_FENABLED) || !f->iffeatures) {
498
            /* disabled feature or no if-features to check */
499
0
            continue;
500
0
        }
501
502
0
        assert(f->iffeatures_c);
503
0
        r = lysc_iffeature_value(f->iffeatures_c);
504
0
        if (r == LY_ENOT) {
505
0
            LOGWRN(pmod->mod->ctx, "Feature \"%s\" cannot be enabled because its \"if-feature\" is not satisfied.",
506
0
                    f->name);
507
508
            /* disable feature and re-evaluate all the feature if-features again */
509
0
            f->flags &= ~LYS_FENABLED;
510
0
            return lys_check_features(pmod);
511
0
        } else if (r) {
512
0
            return r;
513
0
        } /* else if-feature satisfied */
514
0
    }
515
516
0
    return LY_SUCCESS;
517
0
}
518
519
LY_ERR
520
lys_enable_features(struct lysp_module *pmod, const char **features)
521
0
{
522
0
    uint32_t i = 0;
523
0
    struct lysp_feature *f = 0;
524
525
0
    if (!features || !features[0]) {
526
        /* keep all features disabled */
527
0
        return LY_SUCCESS;
528
0
    }
529
530
0
    if (!strcmp(features[0], "*")) {
531
        /* enable all features */
532
0
        while ((f = lysp_feature_next(f, pmod, &i))) {
533
0
            f->flags |= LYS_FENABLED;
534
0
        }
535
0
    } else {
536
        /* enable selected features */
537
0
        for (i = 0; features[i]; ++i) {
538
            /* find the feature */
539
0
            f = lysp_feature_find(pmod, features[i], strlen(features[i]), 0);
540
0
            if (!f) {
541
0
                LOGERR(pmod->mod->ctx, LY_ENOTFOUND, "Feature \"%s\" not found in module \"%s\".", features[i],
542
0
                        pmod->mod->name);
543
0
                return LY_ENOTFOUND;
544
0
            }
545
546
            /* enable feature */
547
0
            f->flags |= LYS_FENABLED;
548
0
        }
549
0
    }
550
551
    /* check final features if-feature state */
552
0
    return lys_check_features(pmod);
553
0
}
554
555
LY_ERR
556
lys_set_features(struct lysp_module *pmod, const char **features)
557
0
{
558
0
    uint32_t i = 0, j;
559
0
    struct lysp_feature *f = 0;
560
0
    ly_bool change = 0;
561
562
0
    if (!features) {
563
        /* do not touch the features */
564
565
0
    } else if (!features[0]) {
566
        /* disable all the features */
567
0
        while ((f = lysp_feature_next(f, pmod, &i))) {
568
0
            if (f->flags & LYS_FENABLED) {
569
0
                f->flags &= ~LYS_FENABLED;
570
0
                change = 1;
571
0
            }
572
0
        }
573
0
    } else if (!strcmp(features[0], "*")) {
574
        /* enable all the features */
575
0
        while ((f = lysp_feature_next(f, pmod, &i))) {
576
0
            if (!(f->flags & LYS_FENABLED)) {
577
0
                f->flags |= LYS_FENABLED;
578
0
                change = 1;
579
0
            }
580
0
        }
581
0
    } else {
582
        /* enable specific features, disable the rest */
583
0
        while ((f = lysp_feature_next(f, pmod, &i))) {
584
0
            for (j = 0; features[j]; ++j) {
585
0
                if (!strcmp(f->name, features[j])) {
586
0
                    break;
587
0
                }
588
0
            }
589
590
0
            if (features[j] && !(f->flags & LYS_FENABLED)) {
591
                /* enable */
592
0
                f->flags |= LYS_FENABLED;
593
0
                change = 1;
594
0
            } else if (!features[j] && (f->flags & LYS_FENABLED)) {
595
                /* disable */
596
0
                f->flags &= ~LYS_FENABLED;
597
0
                change = 1;
598
0
            }
599
0
        }
600
0
    }
601
602
0
    if (!change) {
603
        /* features already set correctly */
604
0
        return LY_EEXIST;
605
0
    }
606
607
    /* check final features if-feature state */
608
0
    return lys_check_features(pmod);
609
0
}
610
611
/**
612
 * @brief Check circular dependency of features - feature MUST NOT reference itself (via their if-feature statement).
613
 *
614
 * The function works in the same way as lys_compile_identity_circular_check() with different structures and error messages.
615
 *
616
 * @param[in] ctx Compile context for logging.
617
 * @param[in] feature The feature referenced in if-feature statement (its depfeatures list is being extended by the feature
618
 *            being currently processed).
619
 * @param[in] depfeatures The list of depending features of the feature being currently processed (not the one provided as @p feature)
620
 * @return LY_SUCCESS if everything is ok.
621
 * @return LY_EVALID if the feature references indirectly itself.
622
 */
623
static LY_ERR
624
lys_compile_feature_circular_check(const struct ly_ctx *ctx, struct lysp_feature *feature, struct lysp_feature **depfeatures)
625
0
{
626
0
    LY_ERR ret = LY_SUCCESS;
627
0
    LY_ARRAY_COUNT_TYPE u, v;
628
0
    struct ly_set recursion = {0};
629
0
    struct lysp_feature *drv;
630
631
0
    if (!depfeatures) {
632
0
        return LY_SUCCESS;
633
0
    }
634
635
0
    for (u = 0; u < LY_ARRAY_COUNT(depfeatures); ++u) {
636
0
        if (feature == depfeatures[u]) {
637
0
            LOGVAL(ctx, LYVE_REFERENCE, "Feature \"%s\" is indirectly referenced from itself.", feature->name);
638
0
            ret = LY_EVALID;
639
0
            goto cleanup;
640
0
        }
641
0
        ret = ly_set_add(&recursion, depfeatures[u], 0, NULL);
642
0
        LY_CHECK_GOTO(ret, cleanup);
643
0
    }
644
645
0
    for (v = 0; v < recursion.count; ++v) {
646
0
        drv = recursion.objs[v];
647
0
        for (u = 0; u < LY_ARRAY_COUNT(drv->depfeatures); ++u) {
648
0
            if (feature == drv->depfeatures[u]) {
649
0
                LOGVAL(ctx, LYVE_REFERENCE, "Feature \"%s\" is indirectly referenced from itself.", feature->name);
650
0
                ret = LY_EVALID;
651
0
                goto cleanup;
652
0
            }
653
0
            ly_set_add(&recursion, drv->depfeatures[u], 0, NULL);
654
0
            LY_CHECK_GOTO(ret, cleanup);
655
0
        }
656
0
    }
657
658
0
cleanup:
659
0
    ly_set_erase(&recursion, NULL);
660
0
    return ret;
661
0
}
662
663
LY_ERR
664
lys_compile_feature_iffeatures(struct lysp_module *pmod)
665
0
{
666
0
    LY_ARRAY_COUNT_TYPE u, v;
667
0
    struct lysp_feature *f = NULL, **df;
668
0
    uint32_t idx = 0;
669
670
0
    while ((f = lysp_feature_next(f, pmod, &idx))) {
671
0
        if (!f->iffeatures) {
672
0
            continue;
673
0
        }
674
675
        /* compile if-features */
676
0
        LY_ARRAY_CREATE_RET(pmod->mod->ctx, f->iffeatures_c, LY_ARRAY_COUNT(f->iffeatures), LY_EMEM);
677
0
        LY_ARRAY_FOR(f->iffeatures, u) {
678
0
            LY_ARRAY_INCREMENT(f->iffeatures_c);
679
0
            LY_CHECK_RET(lys_compile_iffeature(pmod->mod->ctx, &(f->iffeatures)[u], &(f->iffeatures_c)[u]));
680
0
        }
681
0
        LY_ARRAY_FOR(f->iffeatures_c, u) {
682
0
            LY_ARRAY_FOR(f->iffeatures_c[u].features, v) {
683
                /* check for circular dependency - direct reference first,... */
684
0
                if (f == f->iffeatures_c[u].features[v]) {
685
0
                    LOGVAL(pmod->mod->ctx, LYVE_REFERENCE, "Feature \"%s\" is referenced from itself.", f->name);
686
0
                    return LY_EVALID;
687
0
                }
688
                /* ... and indirect circular reference */
689
0
                LY_CHECK_RET(lys_compile_feature_circular_check(pmod->mod->ctx, f->iffeatures_c[u].features[v], f->depfeatures));
690
691
                /* add itself into the dependants list */
692
0
                LY_ARRAY_NEW_RET(pmod->mod->ctx, f->iffeatures_c[u].features[v]->depfeatures, df, LY_EMEM);
693
0
                *df = f;
694
0
            }
695
0
        }
696
0
    }
697
698
0
    return LY_SUCCESS;
699
0
}
700
701
void
702
lys_free_feature_iffeatures(struct lysp_module *pmod)
703
0
{
704
0
    struct lysp_feature *f = NULL;
705
0
    uint32_t idx = 0;
706
707
0
    while ((f = lysp_feature_next(f, pmod, &idx))) {
708
0
        FREE_ARRAY(pmod->mod->ctx, f->iffeatures_c, lysc_iffeature_free);
709
0
        f->iffeatures_c = NULL;
710
0
        LY_ARRAY_FREE(f->depfeatures);
711
        f->depfeatures = NULL;
712
0
    }
713
0
}