genconf: properly check retval of confnode_strvalue_to_boolvalue()
[libcmdline.git] / src / genconf / confnode.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 <sys/queue.h>
32
33 #include "strictmalloc.h"
34 #include "parser_common.h"
35 #include "expression.h"
36 #include "conf_parser.h"
37 #include "confnode.h"
38
39 extern void confnode_root_register(void);
40 extern void confnode_menu_register(void);
41 extern void confnode_config_register(void);
42 extern void confnode_intconfig_register(void);
43 extern void confnode_menuconfig_register(void);
44 extern void confnode_strconfig_register(void);
45 extern void confnode_choice_register(void);
46 extern void confnode_choiceconfig_register(void);
47 extern void confnode_if_register(void);
48 extern void confnode_comment_register(void);
49
50 /* global list of all configuration node types */
51 struct confnode_type_list confnode_type_list;
52
53 /* alloc a new confnode */
54 static struct confnode *confnode_alloc(void)
55 {
56         struct confnode *n;
57
58         n = strictmalloc(sizeof(struct confnode));
59         TAILQ_INIT(&n->children);
60         TAILQ_INIT(&n->depend);
61         snprintf(n->name, sizeof(n->name), "NONAME");
62         snprintf(n->name, sizeof(n->name), "No prompt");
63         snprintf(n->help, sizeof(n->help), "No help available\n");
64
65         return n;
66 }
67
68 /* alloc a new root node */
69 struct confnode *confnode_new_root(void)
70 {
71         struct line l;
72         struct token t;
73
74         /* forge a dummy line */
75         t.str = "root";
76         t.offset = 0;
77         l.start_with_space = 0;
78         l.n_token = 1;
79         TAILQ_INIT(&l.token_list);
80         TAILQ_INSERT_TAIL(&l.token_list, &t, next);
81
82         return confnode_new(&l);
83 }
84
85 /* Parse the line, and return a new confnode if the line is a valid
86  * new confnode line (example: "menuconfig FOO\n"). Else, return
87  * NULL. */
88 struct confnode *confnode_new(const struct line *line)
89 {
90         struct confnode_type *cnt;
91         struct confnode *n;
92
93         n = confnode_alloc();
94
95         TAILQ_FOREACH(cnt, &confnode_type_list, next) {
96                 if (cnt->ops.new == NULL) {
97                         printf("No new() for confnode type\n");
98                         exit(1);
99                 }
100                 if (cnt->ops.new(n, line) == 0)
101                         return n;
102         }
103
104         /* fail */
105         free(n);
106         return NULL;
107 }
108
109 /* Free the node given as argument, and all its associated data */
110 void confnode_free(struct confnode *n)
111 {
112         struct expr_node *e;
113
114         /* specific free */
115         if (n->ops->free) {
116                 n->ops->free(n);
117                 return;
118         }
119
120         while ( (e = TAILQ_FIRST(&n->depend)) ) {
121                 expression_free(e);
122         }
123         free(n);
124 }
125
126 /* Parse a line structure (and its associated line buffer) to match an
127  * attribute of a confnode. Return 0 if the line matches, else return
128  * -1 if we cannot match a valid attribute. */
129 int confnode_add_attr(struct confnode *n, const struct line *line,
130                       const char *linebuf)
131 {
132         struct token *tok;
133
134         tok = TAILQ_FIRST(&line->token_list);
135         if (tok == NULL)
136                 return -1; /* should not happen */
137
138         /* specific attribute */
139         if (n->ops->add_attr)
140                 return n->ops->add_attr(n, line, linebuf);
141
142         /* syntax is: prompt "prompt content" */
143         if (!(n->flags & CONFNODE_F_NO_SET_PROMPT) &&
144             !strcmp(tok->str, "prompt") &&
145             line->n_token <= 2) {
146                 if (line->n_token == 2) {
147                         tok = TAILQ_NEXT(tok, next);
148                         snprintf(n->prompt, sizeof(n->prompt),
149                                  "%s", tok->str);
150                 }
151                 return 0;
152         }
153         /* syntax is: prompt "requires EXPRESSION" */
154         else if (!(n->flags & CONFNODE_F_NO_SET_DEPS) &&
155                  !strcmp(tok->str, "requires") &&
156                  line->n_token > 1) {
157                 struct expr_node *e;
158                 char *tmp;
159                 tok = TAILQ_NEXT(tok, next);
160                 tmp = strdup(linebuf + tok->offset);
161                 e = parse_expression(tmp);
162                 free(tmp);
163                 if (e == NULL)
164                         return -1;
165                 TAILQ_INSERT_TAIL(&n->depend, e, next);
166                 return 0;
167         }
168         /* XXX we could support expression here ? */
169         /* syntax is: prompt "default VALUE" */
170         else if (!(n->flags & CONFNODE_F_NO_SET_DEFAULT) &&
171                  !strcmp(tok->str, "default") &&
172                  line->n_token == 2) {
173                 tok = TAILQ_NEXT(tok, next);
174                 snprintf(n->default_value,
175                          sizeof(n->default_value),
176                          "%s", tok->str);
177                 return 0;
178         }
179
180         return -1;
181 }
182
183 /* Parse a line and check if it closes the current node ,
184  * "endmenu" closes "menu" node): in this case return 0. Else, return
185  * -1. */
186 int confnode_close_dir(struct confnode *parent, const struct line *line)
187 {
188         if (parent->ops->close_dir)
189                 return parent->ops->close_dir(parent, line);
190
191         return -1;
192 }
193
194 /* write config value in file f in a dotconfig-like manner. Return 0
195  * on success. */
196 int confnode_dotconfig_write(const struct confnode *n, FILE *f)
197 {
198         int val;
199         const struct confnode *c;
200         char buf[BUFSIZ];
201         const char *quote = "";
202
203         if (n->ops->dotconfig_write)
204                 return n->ops->dotconfig_write(n, f);
205
206         if (confnode_get_value(n, buf, sizeof(buf)) < 0)
207                 return -1;
208         val = confnode_strvalue_to_boolvalue(n, buf);
209         if (val < 0)
210                 return -1;
211
212         if (val == 0) {
213                 if (fprintf(f, "# CONFIG_%s is not set\n", n->name) < 0)
214                         return -1;
215                 return 0;
216         }
217
218         if (n->flags & CONFNODE_F_QUOTE_VALUE)
219                 quote = "\"";
220         if (fprintf(f, "CONFIG_%s=%s%s%s\n", n->name, quote, buf, quote) < 0)
221                 return -1;
222
223         TAILQ_FOREACH(c, &n->children, next) {
224                 if (confnode_dotconfig_write(c, f) < 0)
225                         return -1;
226         }
227         return 0;
228 }
229
230 /* Convert string value into boolean value (mainly used for
231  * dependancies). Return -1 on error, else return the boolean value (0
232  * or 1). */
233 int confnode_strvalue_to_boolvalue(const struct confnode *n, const char *strvalue)
234 {
235         if (n->ops->strvalue_to_boolvalue)
236                 return n->ops->strvalue_to_boolvalue(n, strvalue);
237
238         if (strcmp("n", strvalue) == 0)
239                 return 0;
240         if (strcmp("y", strvalue) == 0)
241                 return 1;
242         return -1;
243 }
244
245 /* Set the string value of the node n. Return 0 on success, or -1 if
246  * the value cannot be set. */
247 int confnode_set_user_strvalue(struct confnode *n, const char *strvalue)
248 {
249         int start = 0, end;
250         char buf[BUFSIZ];
251
252         buf[BUFSIZ-1] = '\0';
253         strncpy(buf, strvalue, BUFSIZ-1);
254
255         if (remove_quote(buf, &start, &end) == 0)
256                 buf[end] = '\0';
257
258         if (n->ops->set_user_strvalue)
259                 return n->ops->set_user_strvalue(n, buf);
260
261         return -1;
262 }
263
264 /* Get the string value that the user asked to set for this node
265  * 'n'. This function does not check if dependancies are met. Return 0
266  * on success, in this case the string is copied in buffer 'buf' of
267  * len 'buflen'. Return -1 if the value cannot be read. */
268 int confnode_get_user_strvalue(const struct confnode *n, char *buf, unsigned buflen)
269 {
270         if (n->ops->get_user_strvalue)
271                 return n->ops->get_user_strvalue(n, buf, buflen);
272
273         return -1;
274 }
275
276 /* Get user boolean value of the node. Return 0 if disabled, 1 if
277  * enabled, and -1 on error. */
278 int confnode_get_user_boolvalue(const struct confnode *n)
279 {
280         char buf[BUFSIZ];
281         int ret;
282
283         ret = confnode_get_user_strvalue(n, buf, sizeof(buf));
284         if (ret < 0)
285                 return -1;
286         ret = confnode_strvalue_to_boolvalue(n, buf);
287         if (ret < 0)
288                 return -1;
289
290         return ret;
291 }
292
293 /* Set the default string value of the node n. Return 0 on success, or
294  * -1 if the value cannot be set. */
295 int confnode_set_default_strvalue(struct confnode *n, const char *strvalue)
296 {
297         int start = 0, end;
298         char buf[BUFSIZ];
299
300         buf[BUFSIZ-1] = '\0';
301         strncpy(buf, strvalue, BUFSIZ-1);
302
303         if (remove_quote(buf, &start, &end) == 0)
304                 buf[end] = '\0';
305
306         if (n->ops->set_default_strvalue)
307                 return n->ops->set_default_strvalue(n, buf);
308
309         return -1;
310 }
311
312 /* Get the default string value for this node 'n'. This function does
313  * not check if dependancies are met. Return 0 on success, in this
314  * case the string is copied in buffer 'buf' of len 'buflen'. Return
315  * -1 if the value cannot be read. */
316 int confnode_get_default_strvalue(const struct confnode *n, char *buf, unsigned buflen)
317 {
318         if (n->ops->get_default_strvalue)
319                 return n->ops->get_default_strvalue(n, buf, buflen);
320
321         return -1;
322 }
323
324 /*
325  * Check deps to see if a node 'n' can be enabled. If yes, return 1,
326  * else return 0. On error, return -1. If 'verbose' is true, dump an
327  * error for each condition preventing node to be enabled.
328  */
329 int confnode_check_deps(const struct confnode *n, int verbose)
330 {
331         char buf[BUFSIZ];
332         const struct confnode *pn = n->parent;
333         struct expr_node *e;
334         int ret = 1;
335         const char *parent_name;
336
337         /* bypass dependency checks if this flag is set */
338         if (n->flags & CONFNODE_F_NO_DEPS)
339                 return 1;
340
341         /* check that there is no disabled "if" or "menuconfig" above
342          * our node */
343         for (pn = n->parent; pn; pn = pn->parent) {
344                 if (pn->flags & CONFNODE_F_IS_ROOT)
345                         break;
346                 if (!(pn->flags & CONFNODE_F_FORCE_CHILD_DEPS))
347                         continue;
348                 if (confnode_get_boolvalue(pn) != 0)
349                         continue;
350
351                 if (verbose) {
352                         /* use value if node has no name */
353                         if (pn->flags & CONFNODE_F_NO_NAME)
354                                 parent_name = pn->value;
355                         else
356                                 parent_name = pn->name;
357                         printf("cannot enable %s, depends on "
358                                "%s parent %s\n",
359                                n->name,
360                                confnode_get_type_str(pn),
361                                parent_name);
362                 }
363                 ret = 0;
364         }
365
366         /* check deps */
367         TAILQ_FOREACH(e, &n->depend, next) {
368                 if (expression_eval(e))
369                         continue;
370
371                 if (expression_to_str(e, buf, sizeof(buf)) < 0)
372                         return -1;
373                 if (verbose)
374                         printf("cannot enable %s, depends on %s\n", n->name, buf);
375                 ret = 0;
376         }
377
378         return ret;
379 }
380
381 /* Get string value of a node, including dep check. Return -1 on
382  * error. Return the boolean value of the node (0 or 1) on success: in
383  * this case, and if buf is not NULL, buf is filled with the strvalue. */
384 int confnode_get_value(const struct confnode *n, char *buf, unsigned buflen)
385 {
386         int dep, ret, strvalue_len;
387         char localbuf[BUFSIZ];
388         char *strvalue;
389
390         if (buf) {
391                 strvalue = buf;
392                 strvalue_len = buflen;
393         }
394         else {
395                 strvalue = localbuf;
396                 strvalue_len = sizeof(localbuf);
397         }
398
399         /* check deps first */
400         dep = confnode_check_deps(n, 0);
401         if (dep < 0)
402                 return -1;
403         if (dep == 0)
404                 return 0;
405
406         ret = confnode_get_user_strvalue(n, strvalue, strvalue_len);
407         if (ret < 0)
408                 ret = confnode_get_default_strvalue(n, strvalue, strvalue_len);
409         if (ret < 0)
410                 return 0; /* consider the value as false */
411
412         ret = confnode_strvalue_to_boolvalue(n, strvalue);
413         if (ret < 0)
414                 return -1;
415
416         return ret;
417 }
418
419 /* Get boolean value of a node, including dep check. Return -1 on
420  * error. Return the boolean value of the node (0 or 1) on success. */
421 int confnode_get_boolvalue(const struct confnode *n)
422 {
423         return confnode_get_value(n, NULL, 0);
424 }
425
426
427 /* Return a string identifying the node type ("config", "menuconfig",
428  * "choice", ...) */
429 const char *confnode_get_type_str(const struct confnode *n)
430 {
431         if (n->ops->get_type_str)
432                 return n->ops->get_type_str(n);
433         return "none";
434 }
435
436 /* Print a one-line summary of the node. Used in case of 'ls'. */
437 void confnode_display_short(const struct confnode *n)
438 {
439         if (n->ops->display_short)
440                 n->ops->display_short(n);
441         else
442                 printf("------ %s\n", n->prompt);
443 }
444
445 /* Print a detailed view of the node. */
446 void confnode_display_long(const struct confnode *n)
447 {
448         const char *nodetype;
449         char value[MAX_VALUE_SIZE];
450         const char *quote = "";
451
452         if (n->ops->display_long) {
453                 n->ops->display_long(n);
454                 return;
455         }
456
457         value[0] = '\0'; /* XXX get_value */
458         nodetype = confnode_get_type_str(n);
459         printf("%s <%s>\n", nodetype, n->name);
460         printf("    path ");
461         conf_display_path(n);
462
463         if (n->flags & CONFNODE_F_QUOTE_VALUE)
464                 quote = "\"";
465         printf("    value %s%s%s\n", quote, value, quote);
466         printf("    wanted %s%s%s\n", quote, n->value, quote);
467         printf("    default %s%s%s\n", quote, n->default_value, quote);
468         confnode_check_deps(n, 1);
469 }
470
471 void __conf_display_path(const struct confnode *n)
472 {
473         if (n->parent)
474                 __conf_display_path(n->parent);
475         if (n->flags & CONFNODE_F_IS_ROOT)
476                 printf("/");
477         else if (!(n->flags & CONFNODE_F_INVISIBLE))
478                 printf("%s/", n->name);
479 }
480
481 /* show path: used by the 'pwd' command. Nodes like 'if' are
482  * ignored. */
483 void conf_display_path(const struct confnode *n)
484 {
485         __conf_display_path(n);
486         printf("\n");
487 }
488
489 void confnode_register_all(void)
490 {
491         TAILQ_INIT(&confnode_type_list);
492         confnode_root_register();
493         confnode_menu_register();
494         confnode_config_register();
495         confnode_intconfig_register();
496         confnode_menuconfig_register();
497         confnode_strconfig_register();
498         confnode_choice_register();
499         confnode_choiceconfig_register();
500         confnode_if_register();
501         confnode_comment_register();
502 }