initial revision
[ucgine.git] / tools / cfzy / libconfizery / cfzy_confnode_choice.c
1 /*
2  * Copyright (c) 2010, 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 <ctype.h>
32 #include <unistd.h>
33 #include <sys/queue.h>
34
35 #include "cfzy_log.h"
36 #include "cfzy_list.h"
37 #include "cfzy_expr.h"
38 #include "cfzy_confnode.h"
39
40 #define LOG(level, fmt, args...)                                \
41         CFZY_LOG("confnode_choice", level, fmt, ##args)
42
43 /* Return a string identifying the node type ("config", "menuconfig",
44  * "choice", ...) */
45 static const char *choice_get_type_str(const struct cfzy_confnode *n)
46 {
47         (void)n;
48         return "choice";
49 }
50
51 static int choice_str2bool(const struct cfzy_confnode *n, const char *value)
52 {
53         struct cfzy_confnode *c;
54
55         /* NULL means "disabled" */
56         if (value == NULL)
57                 return 0;
58
59         /* if we have no children, it's because we are creating the
60          * node, so accept all values, we will check them later in
61          * choice_close() */
62         if (TAILQ_EMPTY(&n->children))
63                 return 1;
64
65         /* else, the value must be the name of a children */
66         TAILQ_FOREACH(c, &n->children, child_next) {
67                 if (!strcmp(value, c->name))
68                         return 1;
69         }
70
71         /* invalid value */
72         return -1;
73 }
74
75 static int choice_close(struct cfzy_confnode *n)
76 {
77         struct cfzy_confnode_defvalue *defval;
78         struct cfzy_list_elt *e;
79         struct cfzy_confnode *c;
80         char exprbuf[512];
81         int ret;
82
83         /* browse all default values and call choice_str2bool() on
84          * them to check that they are correct */
85         TAILQ_FOREACH(e, n->default_val_list, next) {
86                 defval = e->ptr;
87
88                 if (choice_str2bool(n, defval->val) < 0) {
89                         LOG(ERR, "invalid default value <%s> for node <%s>\n",
90                             defval->val, n->name);
91                         return -1;
92                 }
93         }
94         if (choice_str2bool(n, n->default_value) < 0) {
95                 LOG(ERR, "invalid default value <%s> for node <%s>\n",
96                     n->default_value, n->name);
97                 return -1;
98         }
99
100         /* for each child node */
101         TAILQ_FOREACH(c, &n->children, child_next) {
102
103                 /* set default value */
104                 if (!strcmp(n->default_value, c->name))
105                         c->default_value = strdup("y");
106                 else
107                         c->default_value = strdup("n");
108
109                 if (c->default_value == NULL) {
110                         LOG(ERR, "cannot allocate default value>\n");
111                         return -1;
112                 }
113
114                 /* set conditional default values for child nodes with
115                  * the same condition */
116                 TAILQ_FOREACH(e, n->default_val_list, next) {
117                         defval = e->ptr;
118
119                         if (cfzy_expr_to_str(defval->expr, exprbuf,
120                                              sizeof(exprbuf)) < 0) {
121                                 LOG(ERR, "cannot convert expression to str\n");
122                                 return -1;
123                         }
124
125                         if (!strcmp(defval->val, c->name))
126                                 ret = cfzy_confnode_add_defval(c, "y", exprbuf);
127                         else
128                                 ret = cfzy_confnode_add_defval(c, "n", exprbuf);
129
130                         if (ret < 0) {
131                                 LOG(ERR, "cannot add conditional default\n");
132                                 return -1;
133                         }
134                 }
135
136         }
137
138         return 0;
139 }
140
141 static int choice_set_uservalue(struct cfzy_confnode *n, const char *value)
142 {
143         struct cfzy_confnode *c;
144         char *newvalue;
145
146         /* if value is NULL, unset this node and all its children,
147          * falling back to default value */
148         if (value == NULL) {
149                 /* this node */
150                 if (n->user_value != NULL)
151                         free(n->user_value);
152                 n->user_value = NULL;
153
154                 /* children */
155                 TAILQ_FOREACH(c, &n->children, child_next) {
156                         if (c->user_value != NULL)
157                                 free(c->user_value);
158                         c->user_value = NULL;
159                 }
160                 return 0;
161         }
162
163         /* if not NULL, the value must be the name of a children */
164         TAILQ_FOREACH(c, &n->children, child_next) {
165                 if (!strcmp(value, c->name))
166                         break;
167         }
168         if (c == NULL)
169                 return -1;
170
171
172         /* this node */
173         if (n->user_value != NULL)
174                 free(n->user_value);
175         n->user_value = strdup(c->name);
176         if (n->user_value == NULL) {
177                 LOG(ERR, "cannot allocate value\n");
178                 return -1;
179         }
180
181         /* children */
182         TAILQ_FOREACH(c, &n->children, child_next) {
183                 if (!strcmp(value, c->name))
184                         newvalue = strdup("y");
185                 else
186                         newvalue = strdup("n");
187
188                 if (newvalue == NULL) {
189                         LOG(ERR, "cannot allocate value\n");
190                         return -1;
191                 }
192
193                 if (c->user_value != NULL)
194                         free(c->user_value);
195                 c->user_value = newvalue;
196         }
197
198         return 0;
199 }
200
201 static struct cfzy_confnode_ops choice_ops = {
202         .free = NULL,
203         .add_attr = NULL,
204         .close = choice_close,
205         .dotconfig_write = NULL,
206         .c_hdr_write = NULL,
207         .str2bool = choice_str2bool,
208         .get_type_str = choice_get_type_str,
209         .set_uservalue = choice_set_uservalue,
210 };
211
212 /* Create a new node */
213 enum cfzy_parse_return cfzy_confnode_choice_new(struct cfzy_confnode *n,
214                                                 const struct cfzy_token_list *tklist)
215 {
216         struct cfzy_token *tok;
217         tok = TAILQ_FIRST(&tklist->list);
218
219         if (strcmp(tok->str, "choice") || tklist->n_token != 2)
220                 return NO_MATCH;
221
222         tok = TAILQ_NEXT(tok, next);
223         n->ops = &choice_ops;
224         n->flags = CFZY_F_IS_DIR | CFZY_F_QUOTE_VAL;
225
226         n->name = strdup(tok->str);
227         if (n->name == NULL)
228                 return ERROR;
229
230         return SUCCESS;
231 }