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