initial revision
[ucgine.git] / tools / cfzy / libconfizery / cfzy_confnode.c
1 /*
2  * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
3  * All rights reserved.
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <sys/queue.h>
32
33 #include "cfzy_log.h"
34 #include "cfzy_list.h"
35 #include "cfzy_htable.h"
36 #include "cfzy_string.h"
37 #include "cfzy_expr.h"
38 #include "cfzy_confnode.h"
39 #include "cfzy_conftree.h"
40
41 #define LOG(level, fmt, args...)                                \
42         CFZY_LOG("confnode", level, fmt, ##args)
43
44 /* alloc a new confnode */
45 struct cfzy_confnode *cfzy_confnode_alloc(struct cfzy_conftree *conftree)
46 {
47         struct cfzy_confnode *n;
48
49         n = malloc(sizeof(struct cfzy_confnode));
50         if (n == NULL)
51                 return NULL;
52
53         memset(n, 0, sizeof(*n));
54         TAILQ_INIT(&n->children);
55
56         n->conftree = conftree;
57
58         n->expr_deps = cfzy_list_alloc();
59         if (n->expr_deps == NULL) {
60                 free(n);
61                 return NULL;
62         }
63
64         n->default_val_list = cfzy_list_alloc();
65         if (n->default_val_list == NULL) {
66                 cfzy_list_free(n->expr_deps, NULL);
67                 free(n);
68                 return NULL;
69         }
70
71         return n;
72 }
73
74 /* Free the node given as argument, and all its associated data */
75 void cfzy_confnode_free(struct cfzy_confnode *n)
76 {
77         struct cfzy_confnode *c;
78         struct cfzy_list_elt *e;
79         struct cfzy_confnode_defvalue *defval;
80
81         /* free the children */
82         while ((c = TAILQ_FIRST(&n->children)) != NULL) {
83                 TAILQ_REMOVE(&n->children, c, child_next);
84                 cfzy_confnode_free(c);
85         }
86
87         /* do the specific free first */
88         if (n->ops && n->ops->free)
89                 n->ops->free(n);
90
91         /* free the deps expression list */
92         cfzy_list_free(n->expr_deps, (void *)cfzy_expr_free);
93
94         /* free the deps expression list (manually free each element
95          * instead of providing a function) */
96         while ((e = TAILQ_FIRST(n->default_val_list)) != NULL) {
97                 TAILQ_REMOVE(n->default_val_list, e, next);
98                 defval = e->ptr;
99                 cfzy_expr_free(defval->expr);
100                 free(defval->val);
101                 free(defval);
102                 free(e);
103         }
104         cfzy_list_free(n->default_val_list, NULL);
105
106         if (n->node_deps != NULL)
107                 cfzy_list_free(n->node_deps, NULL);
108
109         /* free the name, help, prompt, ... */
110         if (n->name)
111                 free(n->name);
112         if (n->prompt)
113                 free(n->prompt);
114         if (n->help)
115                 free(n->help);
116         if (n->default_value)
117                 free(n->default_value);
118         if (n->user_value)
119                 free(n->user_value);
120         if (n->effective_value)
121                 free(n->effective_value);
122         if (n->old_user_value)
123                 free(n->old_user_value);
124         if (n->old_effective_value)
125                 free(n->old_effective_value);
126         if (n->filename)
127                 free(n->filename);
128
129         free(n);
130 }
131
132 /* return the list of nodes required to enable this one */
133 struct cfzy_list_head *
134 cfzy_confnode_get_deplist(const struct cfzy_confnode *n)
135 {
136         struct cfzy_list_head *node_list = NULL, *var_list= NULL;
137         struct cfzy_list_elt *e, *e2;
138         struct cfzy_expr *expr;
139         struct cfzy_confnode *c;
140         const char *varname;
141         const struct cfzy_confnode_defvalue *defval;
142         const struct cfzy_conftree *conftree;
143
144         node_list = cfzy_list_alloc();
145         if (node_list == NULL) {
146                 LOG(ERR, "cannot allocate node list\n");
147                 return NULL;
148         }
149
150         /* get associated configuration tree */
151         conftree = n->conftree;
152
153         /* list of dependencies (expression list) */
154         TAILQ_FOREACH(e, n->expr_deps, next) {
155
156                 expr = e->ptr;
157
158                 /* get list of variables for this expression */
159                 var_list = cfzy_expr_get_vars(expr);
160                 if (var_list == NULL) {
161                         LOG(ERR, "cannot allocate get list\n");
162                         goto fail;
163                 }
164
165                 /* list of variables in this expression */
166                 TAILQ_FOREACH(e2, var_list, next) {
167
168                         varname = e2->ptr;
169
170                         c = cfzy_htable_lookup(conftree->nodes_htable, varname);
171                         if (c == NULL) {
172                                 LOG(ERR, "cannot find node <%s>\n", varname);
173                                 goto fail;
174                         }
175
176                         if (cfzy_list_elt_is_in_list(node_list, c))
177                                 continue;
178
179                         if (cfzy_list_add_tail(node_list, c) < 0) {
180                                 LOG(ERR, "cannot add node in list\n");
181                                 goto fail;
182                         }
183                 }
184
185                 cfzy_list_free(var_list, free);
186                 var_list = NULL;
187         }
188
189         /* list of conditional default values depending on an expression */
190         TAILQ_FOREACH(e, n->default_val_list, next) {
191
192                 defval = e->ptr;
193                 expr = defval->expr;
194
195                 /* get list of variables for this expression */
196                 var_list = cfzy_expr_get_vars(expr);
197                 if (var_list == NULL) {
198                         LOG(ERR, "cannot allocate get list\n");
199                         goto fail;
200                 }
201
202                 /* list of variables in this expression */
203                 TAILQ_FOREACH(e2, var_list, next) {
204
205                         varname = e2->ptr;
206
207                         c = cfzy_htable_lookup(conftree->nodes_htable, varname);
208                         if (c == NULL) {
209                                 LOG(ERR, "cannot find node <%s>\n", varname);
210                                 goto fail;
211                         }
212
213                         if (cfzy_list_elt_is_in_list(node_list, c))
214                                 continue;
215
216                         if (cfzy_list_add_tail(node_list, c) < 0) {
217                                 LOG(ERR, "cannot add node in list\n");
218                                 goto fail;
219                         }
220                 }
221
222                 cfzy_list_free(var_list, free);
223                 var_list = NULL;
224         }
225
226         /* parent */
227         if (n->parent != NULL && !cfzy_list_elt_is_in_list(node_list, n->parent)) {
228
229                 if (cfzy_list_add_tail(node_list, n->parent) < 0) {
230                         LOG(ERR, "cannot add node in list\n");
231                         goto fail;
232                 }
233         }
234
235         return node_list;
236
237  fail:
238         if (node_list != NULL)
239                 cfzy_list_free(node_list, NULL);
240         if (var_list != NULL)
241                 cfzy_list_free(var_list, free);
242
243         return NULL;
244 }
245
246 /* Add a conditional default value to a node */
247 int cfzy_confnode_add_defval(struct cfzy_confnode *n, const char *val,
248                              const char *expr_buf)
249 {
250         struct cfzy_confnode_defvalue *defval;
251
252         defval = malloc(sizeof(*defval));
253         if (defval == NULL)
254                 return -1;
255
256         memset(defval, 0, sizeof(*defval));
257         defval->expr = cfzy_expr_parse(expr_buf);
258         if (defval->expr == NULL) {
259                 free(defval);
260                 return -1;
261         }
262
263         defval->val = strdup(val);
264         if (defval->val == NULL) {
265                 cfzy_expr_free(defval->expr);
266                 free(defval);
267                 return -1;
268         }
269
270         if (cfzy_list_add_tail(n->default_val_list, defval) < 0) {
271                 free(defval->val);
272                 cfzy_expr_free(defval->expr);
273                 free(defval);
274                 return -1;
275         }
276
277         return 0;
278 }
279
280 /* Parse a line structure (and its associated line buffer) to match an
281  * attribute of a confnode. Return 0 if the line matches, else return
282  * -1 if we cannot match a valid attribute. */
283 enum cfzy_parse_return
284 cfzy_confnode_add_attr(struct cfzy_confnode *n,
285                        const struct cfzy_token_list *tklist)
286 {
287         enum cfzy_parse_return ret;
288         struct cfzy_token *first_tok, *tok;
289         const char *linebuf = tklist->linebuf;
290
291         first_tok = TAILQ_FIRST(&tklist->list);
292
293         /* specific attribute */
294         if (n->ops->add_attr != NULL) {
295                 ret = n->ops->add_attr(n, tklist, linebuf);
296                 if (ret != NO_MATCH)
297                         return ret;
298         }
299
300         /* syntax is: "prompt content" */
301         if (tklist->n_token == 2 &&
302             !strcmp(first_tok->str, "prompt")) {
303
304                 if (n->flags & CFZY_F_NO_SET_PROMPT) {
305                         LOG(ERR, "node does not support 'prompt' attr\n");
306                         return ERROR;
307                 }
308
309                 tok = TAILQ_NEXT(first_tok, next);
310                 if (n->prompt != NULL) {
311                         free(n->prompt);
312                         n->prompt = NULL;
313                 }
314                 n->prompt = strdup(tok->str);
315                 if (n->prompt == NULL) {
316                         LOG(ERR, "cannot allocate prompt\n");
317                         return ERROR;
318                 }
319                 return SUCCESS;
320         }
321
322         /* syntax is: "requires EXPRESSION" */
323         if (tklist->n_token > 1 &&
324             !strcmp(first_tok->str, "requires")) {
325                 struct cfzy_expr *exp;
326
327                 if (n->flags & CFZY_F_NO_SET_DEPS) {
328                         LOG(ERR, "node does not support 'requires' attr\n");
329                         return ERROR;
330                 }
331
332                 tok = TAILQ_NEXT(first_tok, next);
333                 exp = cfzy_expr_parse(linebuf + tok->offset);
334                 if (exp == NULL) {
335                         LOG(ERR, "cannot parse expression\n");
336                         return ERROR;
337                 }
338
339                 cfzy_list_add_tail(n->expr_deps, exp);
340                 return SUCCESS;
341         }
342
343         /* syntax is: "default VALUE" */
344         if (tklist->n_token == 2 &&
345             !strcmp(first_tok->str, "default")) {
346
347                 if (n->flags & CFZY_F_NO_SET_DEFAULT) {
348                         LOG(ERR, "node does not support 'default' attr\n");
349                         return ERROR;
350                 }
351
352                 tok = TAILQ_NEXT(first_tok, next);
353                 if (cfzy_confnode_str2bool(n, tok->str) < 0) {
354                         LOG(ERR, "invalid value for this node\n");
355                         return ERROR;
356                 }
357
358                 if (n->default_value != NULL)
359                         free(n->default_value);
360                 n->default_value = strdup(tok->str);
361                 if (n->default_value == NULL) {
362                         LOG(ERR, "cannot add default value\n");
363                         return ERROR;
364                 }
365
366                 return SUCCESS;
367         }
368
369         /* syntax is: "default VALUE if EXPR" */
370         if (tklist->n_token >= 4 &&
371             !strcmp(first_tok->str, "default")) {
372                 struct cfzy_token *tok2;
373
374                 tok = TAILQ_NEXT(first_tok, next); /* points to value */
375                 tok2 = TAILQ_NEXT(tok, next); /* points to "if" */
376
377                 if (strcmp(tok2->str, "if"))
378                         return NO_MATCH;
379
380                 if (n->flags & CFZY_F_NO_SET_DEFAULT) {
381                         LOG(ERR, "node does not support 'default' attr\n");
382                         return ERROR;
383                 }
384
385                 tok2 = TAILQ_NEXT(tok2, next); /* points to expression */
386
387                 if (cfzy_confnode_str2bool(n, tok->str) < 0) {
388                         LOG(ERR, "invalid value for this node\n");
389                         return ERROR;
390                 }
391
392                 if (cfzy_confnode_add_defval(n, tok->str,
393                                              linebuf + tok2->offset) < 0) {
394                         LOG(ERR, "cannot add default value\n");
395                         return ERROR;
396                 }
397
398                 return SUCCESS;
399         }
400
401         return NO_MATCH;
402 }
403
404 /* write config value in file f in a dotconfig-like manner. Return 0
405  * on success. */
406 int cfzy_confnode_dotconfig_write(const struct cfzy_confnode *n, FILE *f)
407 {
408         int val;
409         const struct cfzy_confnode *c;
410
411         if (n->ops->dotconfig_write)
412                 return n->ops->dotconfig_write(n, f);
413
414         if (n->flags & CFZY_F_DOTCONF_SHOW_TITLE) {
415                 if (fprintf(f,
416                             "#\n"
417                             "# -- %s\n"
418                             "#\n", n->prompt) < 0)
419                         return -1;
420         }
421
422         /* only dump the children if effective value is not defined
423          * (some nodes like "comment", "if", "menu", ... can have a
424          * NULL effective value) */
425         if (n->effective_value == NULL)
426                 goto dump_children;
427
428         /* when a boolean is unset, use the "is not set" syntax */
429         if (n->flags & CFZY_F_VAL_IS_BOOL) {
430                 val = cfzy_confnode_str2bool(n, n->effective_value);
431                 if (val < 0)
432                         return -1;
433
434                 if (val == 0) {
435                         if (fprintf(f, "# CONFIG_%s is not set\n",
436                                     cfzy_confnode_name(n)) < 0)
437                                 return -1;
438                         return 0;
439                 }
440         }
441
442         /* common case: dump the value, with or without quotes */
443         if (n->flags & CFZY_F_QUOTE_VAL) {
444                 char *quoted_value = cfzy_string_quote(n->effective_value);
445
446                 if (quoted_value == NULL)
447                         return -1;
448
449                 if (fprintf(f, "CONFIG_%s=%s\n", cfzy_confnode_name(n),
450                             quoted_value) < 0) {
451                         free(quoted_value);
452                         return -1;
453                 }
454                 free(quoted_value);
455         }
456         else {
457                 if (fprintf(f, "CONFIG_%s=%s\n", cfzy_confnode_name(n),
458                             n->effective_value) < 0)
459                         return -1;
460         }
461
462  dump_children:
463         TAILQ_FOREACH(c, &n->children, child_next) {
464                 if (cfzy_confnode_dotconfig_write(c, f) < 0)
465                         return -1;
466         }
467         return 0;
468 }
469
470 /* write config value in file f in a dotconfig-like manner. Return 0
471  * on success. */
472 int cfzy_confnode_c_hdr_write(const struct cfzy_confnode *n, FILE *f)
473 {
474         int val;
475         const struct cfzy_confnode *c;
476
477         if (n->ops->c_hdr_write)
478                 return n->ops->c_hdr_write(n, f);
479
480         /* if no value, don't dump the value of the node, just the children */
481         if (n->effective_value == NULL)
482                 goto dump_children;
483
484         if (n->flags & CFZY_F_VAL_IS_BOOL) {
485                 val = cfzy_confnode_str2bool(n, n->effective_value);
486                 if (val < 0)
487                         return -1;
488
489                 /* bool is false -> undef */
490                 if (val == 0) {
491                         if (fprintf(f, "#undef CONFIG_%s\n",
492                                     cfzy_confnode_name(n)) < 0)
493                                 return -1;
494                         return 0;
495                 }
496
497                 /* bool is true -> #define to 1 */
498                 if (fprintf(f, "#define CONFIG_%s 1\n",
499                             cfzy_confnode_name(n)) < 0)
500                         return -1;
501         }
502         /* common case: dump the value, with or without quotes */
503         else if (n->flags & CFZY_F_QUOTE_VAL) {
504                 char *quoted_value = cfzy_string_quote(n->effective_value);
505
506                 if (quoted_value == NULL)
507                         return -1;
508
509                 if (fprintf(f, "#define CONFIG_%s %s\n",
510                             cfzy_confnode_name(n), quoted_value) < 0) {
511                         free(quoted_value);
512                         return -1;
513                 }
514                 free(quoted_value);
515         }
516         else {
517                 if (fprintf(f, "#define CONFIG_%s %s\n",
518                             cfzy_confnode_name(n), n->effective_value) < 0)
519                         return -1;
520         }
521
522  dump_children:
523         TAILQ_FOREACH(c, &n->children, child_next) {
524                 if (cfzy_confnode_c_hdr_write(c, f) < 0)
525                         return -1;
526         }
527         return 0;
528 }
529
530 /* Close the node beeing parsed */
531 int cfzy_confnode_close(struct cfzy_confnode *n)
532 {
533         if (n->ops->close)
534                 return n->ops->close(n);
535
536         return 0;
537 }
538
539 /* Return the boolean value of the node given the string value */
540 int cfzy_confnode_str2bool(const struct cfzy_confnode *n,
541                                 const char *value)
542 {
543         if (n->ops->str2bool)
544                 return n->ops->str2bool(n, value);
545
546         /* if there is no specific function, any non-NULL value
547          * returns 1 */
548         if (value == NULL)
549                 return 0;
550
551         return 1;
552 }
553
554 /* Return a string identifying the node type ("config", "menuconfig",
555  * "choice", ...) */
556 const char *cfzy_confnode_get_type_str(const struct cfzy_confnode *n)
557 {
558         if (n->ops->get_type_str)
559                 return n->ops->get_type_str(n);
560
561         return "unknown";
562 }
563
564 static int __get_path(const struct cfzy_confnode *n, char *buf,
565                       int len, int first)
566 {
567         int ret = 0, off;
568
569         if (n->parent == NULL)
570                 return snprintf(buf, len, "/");
571
572         ret = __get_path(n->parent, buf, len, 0);
573         if (ret < 0)
574                 return ret;
575         off = ret;
576         if (off >= len)
577                 return -1;
578
579         if (n->flags & CFZY_F_INVISIBLE)
580                 return ret;
581
582         /* a visible node must have a name */
583         if (n->name == NULL)
584                 return -1;
585
586         ret = snprintf(buf + off, len - off, "%s%s", n->name,
587                        first ? "" : "/");
588         if (ret >= len - off)
589                 return -1;
590
591         return ret + off;
592 }
593
594 /* Dump path in buffer. Invisible nodes like 'if' are ignored. */
595 int cfzy_confnode_path(const struct cfzy_confnode *n, char *buf, int len)
596 {
597         int ret;
598
599         ret = __get_path(n, buf, len, 1);
600         if (ret < 0)
601                 return ret;
602         return 0;
603 }
604
605 /* dump a config file in a file */
606 int cfzy_confnode_dump(const struct cfzy_confnode *n, FILE *f, int mode)
607 {
608         const struct cfzy_confnode *c;
609         struct cfzy_confnode_defvalue *defval;
610         struct cfzy_list_elt *e;
611         struct cfzy_expr *expr;
612         char buf[512];
613
614         if (fprintf(f, "%s\n", cfzy_confnode_get_type_str(n)) < 0)
615                 return -1;
616         if (fprintf(f, "  name: %s\n", cfzy_confnode_name(n)) < 0)
617                 return -1;
618         if (fprintf(f, "  prompt: %s\n", n->prompt) < 0)
619                 return -1;
620         if (cfzy_confnode_path(n, buf, sizeof(buf)) == 0) {
621                 if (fprintf(f, "  path: %s\n", buf) < 0)
622                         return -1;
623         }
624
625         if (mode == CFZY_DUMP_MODE_MINIMAL)
626                 return 0;
627
628         /* explicit dependencies */
629         TAILQ_FOREACH(e, n->expr_deps, next) {
630                 expr = e->ptr;
631                 if (cfzy_expr_to_str(expr, buf, sizeof(buf)) < 0)
632                         return -1;
633                 if (fprintf(f, "  requires: %s\n", buf) < 0)
634                         return -1;
635         }
636
637         /* XXX display valid values */
638
639         /* default values */
640         TAILQ_FOREACH(e, n->default_val_list, next) {
641                 defval = e->ptr;
642
643                 if (cfzy_expr_to_str(defval->expr, buf, sizeof(buf)) < 0)
644                         return -1;
645
646                 if (fprintf(f, "  default: %s if %s\n", defval->val, buf) < 0)
647                                 return -1;
648         }
649         if (fprintf(f, "  node_default %s\n", n->default_value) < 0)
650                 return -1;
651         if (fprintf(f, "  user_value %s\n", n->user_value) < 0)
652                 return -1;
653         if (fprintf(f, "  effective_value %s\n", n->effective_value) < 0)
654                 return -1;
655
656         if (mode == CFZY_DUMP_MODE_STANDARD)
657                 return 0;
658
659         if (fprintf(f, "  old_user_value %s\n", n->old_user_value) < 0)
660                 return -1;
661         if (fprintf(f, "  old_effective_value %s\n", n->old_effective_value) < 0)
662                 return -1;
663         if (fprintf(f, "  parsed at %s:%d\n", n->filename, n->linenum) < 0)
664                 return -1;
665
666         if (fprintf(f, "\n") < 0)
667                 return -1;
668
669         if (mode == CFZY_DUMP_MODE_FULL)
670                 return 0;
671
672         /* dump children */
673         TAILQ_FOREACH(c, &n->children, child_next) {
674                 if (cfzy_confnode_dump(c, f, CFZY_DUMP_MODE_FULL_RECURSIVE) < 0)
675                         return -1;
676         }
677
678         /* close node if it's a directory */
679         if (n->flags & CFZY_F_IS_DIR) {
680                 if (fprintf(f, "end%s\n\n", cfzy_confnode_get_type_str(n)) < 0)
681                         return -1;
682         }
683
684         return 0;
685 }
686
687 /* set the uservalue of a node */
688 int cfzy_confnode_set_uservalue(struct cfzy_confnode *n, const char *value)
689 {
690         char *newvalue = NULL;
691
692         if (n->ops->set_uservalue)
693                 return n->ops->set_uservalue(n, value);
694
695         /* check if value is valid */
696         if (cfzy_confnode_str2bool(n, value) < 0)
697                 return -1;
698
699         if (value != NULL) {
700                 newvalue = strdup(value);
701                 if (newvalue == NULL) {
702                         LOG(ERR, "cannot allocate new value\n");
703                         return -1;
704                 }
705         }
706
707         if (n->user_value != NULL)
708                 free(n->user_value);
709
710         n->user_value = newvalue;
711         return 0;
712 }
713
714 /* Get the boolean value of a node, given its name, used as a callback
715  * by cfzy_expr_parse() when parsing an expression for
716  * cfzy_confnode_evaluate() */
717 int cfzy_confnode_get_boolvalue(const char *name,
718                                 const struct cfzy_conftree *conftree)
719 {
720         const struct cfzy_confnode *n;
721
722         n = cfzy_htable_lookup(conftree->nodes_htable, name);
723         if (n == NULL)
724                 return -1;
725
726         return cfzy_confnode_str2bool(n, n->effective_value);
727 }
728
729 /* return 0 if a parent node is disabled or if a prerequisite
730  * expression is false, else return 1. On error, return -1. */
731 int cfzy_confnode_check_deps(struct cfzy_confnode *n)
732 {
733         struct cfzy_list_elt *e;
734         struct cfzy_expr *expr;
735         struct cfzy_confnode *parent;
736         int (*get_boolvalue)(const char *, const struct cfzy_conftree *);
737         int (*get_boolvalue_casted)(const char *, void *);
738         int ret;
739
740         /* we use an intermediate variable to have a compiler warning
741          * if the prototype of cfzy_confnode_get_boolvalue changes. We
742          * know that that these types are compatible here. */
743         get_boolvalue = cfzy_confnode_get_boolvalue;
744         get_boolvalue_casted = (void *)get_boolvalue;
745
746         /* check the dep list first */
747         TAILQ_FOREACH(e, n->expr_deps, next) {
748                 expr = e->ptr;
749
750                 ret = cfzy_expr_eval(expr, get_boolvalue_casted, n->conftree);
751                 if (ret < 0)
752                         return -1;
753
754                 /* the expression is evaluated to 0, we cannot enable
755                  * this node */
756                 if (ret == 0) {
757                         n->effective_value = NULL;
758                         LOG(DEBUG, "disable node <%s> (expr dep)\n",
759                             cfzy_confnode_name(n));
760                         return 0;
761                 }
762         }
763
764         /* check the parent nodes */
765         for (parent = n->parent; parent != NULL; parent = parent->parent) {
766                 ret = cfzy_confnode_str2bool(parent, parent->effective_value);
767                 if (ret < 0)
768                         return -1;
769
770                 /* a parent node is evaluated to 0, we cannot enable
771                  * this node */
772                 if (ret == 0) {
773                         n->effective_value = NULL;
774                         LOG(DEBUG, "disable node <%s> (parent dep)\n",
775                             cfzy_confnode_name(n));
776                         return 0;
777                 }
778         }
779
780         return 1;
781 }
782
783 /* Evaluate the effective value of a node, assuming all prerequisites
784  * are already evaluated. */
785 int cfzy_confnode_evaluate(struct cfzy_confnode *n)
786 {
787         struct cfzy_list_elt *e;
788         struct cfzy_confnode_defvalue *defval;
789         int (*get_boolvalue)(const char *, const struct cfzy_conftree *);
790         int (*get_boolvalue_casted)(const char *, void *);
791         int ret;
792
793         LOG(DEBUG, "evaluating node <%s>\n", cfzy_confnode_name(n));
794
795         /* we use an intermediate variable to have a compiler warning
796          * if the prototype of cfzy_confnode_get_boolvalue changes. We
797          * know that that these types are compatible here. */
798         get_boolvalue = cfzy_confnode_get_boolvalue;
799         get_boolvalue_casted = (void *)get_boolvalue;
800
801         /* remove previous effective value */
802         if (n->effective_value != NULL) {
803                 free(n->effective_value);
804                 n->effective_value = NULL;
805         }
806
807         /* check prerequisites and parents */
808         ret = cfzy_confnode_check_deps(n);
809         if (ret == 0) {
810                 /* we cannot enable this node due to dependencies */
811                 n->effective_value = NULL;
812                 LOG(DEBUG, "disable node <%s> (expr dep)\n",
813                     cfzy_confnode_name(n));
814                 return 0;
815         }
816         /* error */
817         if (ret < 0)
818                 return ret;
819
820         /* user value has the priority */
821         if (n->user_value != NULL) {
822                 n->effective_value = strdup(n->user_value);
823                 if (n->effective_value == NULL)
824                         return -1;
825
826                 LOG(DEBUG, "set node <%s> to user value <%s>\n",
827                     cfzy_confnode_name(n), n->effective_value);
828                 return 0;
829         }
830
831         /* assign a default value */
832         TAILQ_FOREACH(e, n->default_val_list, next) {
833                 defval = e->ptr;
834
835                 ret = cfzy_expr_eval(defval->expr, get_boolvalue_casted,
836                                      n->conftree);
837                 if (ret < 0)
838                         return -1;
839
840                 /* the expression is evaluated to 1, use this default
841                  * value for this node */
842                 if (ret == 1) {
843                         n->effective_value = strdup(defval->val);
844                         if (n->effective_value == NULL)
845                                 return -1;
846                         LOG(DEBUG, "set node <%s> to default_list value <%s>\n",
847                             cfzy_confnode_name(n), n->effective_value);
848                         return 0;
849                 }
850         }
851
852         if (n->default_value != NULL)
853                 n->effective_value = strdup(n->default_value);
854
855         LOG(DEBUG, "set node <%s> to default value <%s>\n",
856             cfzy_confnode_name(n), n->effective_value);
857
858         return 0;
859 }