Initial revision
[openwrt.git] / package / config / menu.c
1 /*
2  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3  * Released under the terms of the GNU GPL v2.0.
4  */
5
6 #include <stdlib.h>
7 #include <string.h>
8
9 #define LKC_DIRECT_LINK
10 #include "lkc.h"
11
12 struct menu rootmenu;
13 struct menu *current_menu, *current_entry;
14 static struct menu **last_entry_ptr;
15
16 struct file *file_list;
17 struct file *current_file;
18
19 static void menu_warn(struct menu *menu, const char *fmt, ...)
20 {
21         va_list ap;
22         va_start(ap, fmt);
23         fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
24         vfprintf(stderr, fmt, ap);
25         fprintf(stderr, "\n");
26         va_end(ap);
27 }
28
29 static void prop_warn(struct property *prop, const char *fmt, ...)
30 {
31         va_list ap;
32         va_start(ap, fmt);
33         fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno);
34         vfprintf(stderr, fmt, ap);
35         fprintf(stderr, "\n");
36         va_end(ap);
37 }
38
39 void menu_init(void)
40 {
41         current_entry = current_menu = &rootmenu;
42         last_entry_ptr = &rootmenu.list;
43 }
44
45 void menu_add_entry(struct symbol *sym)
46 {
47         struct menu *menu;
48
49         menu = malloc(sizeof(*menu));
50         memset(menu, 0, sizeof(*menu));
51         menu->sym = sym;
52         menu->parent = current_menu;
53         menu->file = current_file;
54         menu->lineno = zconf_lineno();
55
56         *last_entry_ptr = menu;
57         last_entry_ptr = &menu->next;
58         current_entry = menu;
59 }
60
61 void menu_end_entry(void)
62 {
63 }
64
65 void menu_add_menu(void)
66 {
67         current_menu = current_entry;
68         last_entry_ptr = &current_entry->list;
69 }
70
71 void menu_end_menu(void)
72 {
73         last_entry_ptr = &current_menu->next;
74         current_menu = current_menu->parent;
75 }
76
77 struct expr *menu_check_dep(struct expr *e)
78 {
79         if (!e)
80                 return e;
81
82         switch (e->type) {
83         case E_NOT:
84                 e->left.expr = menu_check_dep(e->left.expr);
85                 break;
86         case E_OR:
87         case E_AND:
88                 e->left.expr = menu_check_dep(e->left.expr);
89                 e->right.expr = menu_check_dep(e->right.expr);
90                 break;
91         case E_SYMBOL:
92                 /* change 'm' into 'm' && MODULES */
93                 if (e->left.sym == &symbol_mod)
94                         return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
95                 break;
96         default:
97                 break;
98         }
99         return e;
100 }
101
102 void menu_add_dep(struct expr *dep)
103 {
104         current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
105 }
106
107 void menu_set_type(int type)
108 {
109         struct symbol *sym = current_entry->sym;
110
111         if (sym->type == type)
112                 return;
113         if (sym->type == S_UNKNOWN) {
114                 sym->type = type;
115                 return;
116         }
117         menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n",
118             sym->name ? sym->name : "<choice>",
119             sym_type_name(sym->type), sym_type_name(type));
120 }
121
122 struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
123 {
124         struct property *prop = prop_alloc(type, current_entry->sym);
125
126         prop->menu = current_entry;
127         prop->text = prompt;
128         prop->expr = expr;
129         prop->visible.expr = menu_check_dep(dep);
130
131         if (prompt) {
132                 if (current_entry->prompt)
133                         menu_warn(current_entry, "prompt redefined\n");
134                 current_entry->prompt = prop;
135         }
136
137         return prop;
138 }
139
140 void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
141 {
142         menu_add_prop(type, prompt, NULL, dep);
143 }
144
145 void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
146 {
147         menu_add_prop(type, NULL, expr, dep);
148 }
149
150 void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
151 {
152         menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
153 }
154
155 void sym_check_prop(struct symbol *sym)
156 {
157         struct property *prop;
158         struct symbol *sym2;
159         for (prop = sym->prop; prop; prop = prop->next) {
160                 switch (prop->type) {
161                 case P_DEFAULT:
162                         if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
163                             prop->expr->type != E_SYMBOL)
164                                 prop_warn(prop,
165                                     "default for config symbol '%'"
166                                     " must be a single symbol", sym->name);
167                         break;
168                 case P_SELECT:
169                         sym2 = prop_get_symbol(prop);
170                         if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
171                                 prop_warn(prop,
172                                     "config symbol '%s' uses select, but is "
173                                     "not boolean or tristate", sym->name);
174                         else if (sym2->type == S_UNKNOWN)
175                                 prop_warn(prop,
176                                     "'select' used by config symbol '%s' "
177                                     "refer to undefined symbol '%s'",
178                                     sym->name, sym2->name);
179                         else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE)
180                                 prop_warn(prop,
181                                     "'%s' has wrong type. 'select' only "
182                                     "accept arguments of boolean and "
183                                     "tristate type", sym2->name);
184                         break;
185                 case P_RANGE:
186                         if (sym->type != S_INT && sym->type != S_HEX)
187                                 prop_warn(prop, "range is only allowed "
188                                                 "for int or hex symbols");
189                         if (!sym_string_valid(sym, prop->expr->left.sym->name) ||
190                             !sym_string_valid(sym, prop->expr->right.sym->name))
191                                 prop_warn(prop, "range is invalid");
192                         break;
193                 default:
194                         ;
195                 }
196         }
197 }
198
199 void menu_finalize(struct menu *parent)
200 {
201         struct menu *menu, *last_menu;
202         struct symbol *sym;
203         struct property *prop;
204         struct expr *parentdep, *basedep, *dep, *dep2, **ep;
205
206         sym = parent->sym;
207         if (parent->list) {
208                 if (sym && sym_is_choice(sym)) {
209                         /* find the first choice value and find out choice type */
210                         for (menu = parent->list; menu; menu = menu->next) {
211                                 if (menu->sym) {
212                                         current_entry = parent;
213                                         menu_set_type(menu->sym->type);
214                                         current_entry = menu;
215                                         menu_set_type(sym->type);
216                                         break;
217                                 }
218                         }
219                         parentdep = expr_alloc_symbol(sym);
220                 } else if (parent->prompt)
221                         parentdep = parent->prompt->visible.expr;
222                 else
223                         parentdep = parent->dep;
224
225                 for (menu = parent->list; menu; menu = menu->next) {
226                         basedep = expr_transform(menu->dep);
227                         basedep = expr_alloc_and(expr_copy(parentdep), basedep);
228                         basedep = expr_eliminate_dups(basedep);
229                         menu->dep = basedep;
230                         if (menu->sym)
231                                 prop = menu->sym->prop;
232                         else
233                                 prop = menu->prompt;
234                         for (; prop; prop = prop->next) {
235                                 if (prop->menu != menu)
236                                         continue;
237                                 dep = expr_transform(prop->visible.expr);
238                                 dep = expr_alloc_and(expr_copy(basedep), dep);
239                                 dep = expr_eliminate_dups(dep);
240                                 if (menu->sym && menu->sym->type != S_TRISTATE)
241                                         dep = expr_trans_bool(dep);
242                                 prop->visible.expr = dep;
243                                 if (prop->type == P_SELECT) {
244                                         struct symbol *es = prop_get_symbol(prop);
245                                         es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
246                                                         expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
247                                 }
248                         }
249                 }
250                 for (menu = parent->list; menu; menu = menu->next)
251                         menu_finalize(menu);
252         } else if (sym) {
253                 basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
254                 basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
255                 basedep = expr_eliminate_dups(expr_transform(basedep));
256                 last_menu = NULL;
257                 for (menu = parent->next; menu; menu = menu->next) {
258                         dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
259                         if (!expr_contains_symbol(dep, sym))
260                                 break;
261                         if (expr_depends_symbol(dep, sym))
262                                 goto next;
263                         dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
264                         dep = expr_eliminate_dups(expr_transform(dep));
265                         dep2 = expr_copy(basedep);
266                         expr_eliminate_eq(&dep, &dep2);
267                         expr_free(dep);
268                         if (!expr_is_yes(dep2)) {
269                                 expr_free(dep2);
270                                 break;
271                         }
272                         expr_free(dep2);
273                 next:
274                         menu_finalize(menu);
275                         menu->parent = parent;
276                         last_menu = menu;
277                 }
278                 if (last_menu) {
279                         parent->list = parent->next;
280                         parent->next = last_menu->next;
281                         last_menu->next = NULL;
282                 }
283         }
284         for (menu = parent->list; menu; menu = menu->next) {
285                 if (sym && sym_is_choice(sym) && menu->sym) {
286                         menu->sym->flags |= SYMBOL_CHOICEVAL;
287                         if (!menu->prompt)
288                                 menu_warn(menu, "choice value must have a prompt");
289                         for (prop = menu->sym->prop; prop; prop = prop->next) {
290                                 if (prop->type == P_PROMPT && prop->menu != menu) {
291                                         prop_warn(prop, "choice values "
292                                             "currently only support a "
293                                             "single prompt");
294                                 }
295                                 if (prop->type == P_DEFAULT)
296                                         prop_warn(prop, "defaults for choice "
297                                             "values not supported");
298                         }
299                         current_entry = menu;
300                         menu_set_type(sym->type);
301                         menu_add_symbol(P_CHOICE, sym, NULL);
302                         prop = sym_get_choice_prop(sym);
303                         for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
304                                 ;
305                         *ep = expr_alloc_one(E_CHOICE, NULL);
306                         (*ep)->right.sym = menu->sym;
307                 }
308                 if (menu->list && (!menu->prompt || !menu->prompt->text)) {
309                         for (last_menu = menu->list; ; last_menu = last_menu->next) {
310                                 last_menu->parent = parent;
311                                 if (!last_menu->next)
312                                         break;
313                         }
314                         last_menu->next = menu->next;
315                         menu->next = menu->list;
316                         menu->list = NULL;
317                 }
318         }
319
320         if (sym && !(sym->flags & SYMBOL_WARNED)) {
321                 if (sym->type == S_UNKNOWN)
322                         menu_warn(parent, "config symbol defined "
323                             "without type\n");
324
325                 if (sym_is_choice(sym) && !parent->prompt)
326                         menu_warn(parent, "choice must have a prompt\n");
327
328                 /* Check properties connected to this symbol */
329                 sym_check_prop(sym);
330                 sym->flags |= SYMBOL_WARNED;
331         }
332
333         if (sym && !sym_is_optional(sym) && parent->prompt) {
334                 sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
335                                 expr_alloc_and(parent->prompt->visible.expr,
336                                         expr_alloc_symbol(&symbol_mod)));
337         }
338 }
339
340 bool menu_is_visible(struct menu *menu)
341 {
342         struct menu *child;
343         struct symbol *sym;
344         tristate visible;
345
346         if (!menu->prompt)
347                 return false;
348         sym = menu->sym;
349         if (sym) {
350                 sym_calc_value(sym);
351                 visible = menu->prompt->visible.tri;
352         } else
353                 visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
354
355         if (visible != no)
356                 return true;
357         if (!sym || sym_get_tristate_value(menu->sym) == no)
358                 return false;
359
360         for (child = menu->list; child; child = child->next)
361                 if (menu_is_visible(child))
362                         return true;
363         return false;
364 }
365
366 const char *menu_get_prompt(struct menu *menu)
367 {
368         if (menu->prompt)
369                 return menu->prompt->text;
370         else if (menu->sym)
371                 return menu->sym->name;
372         return NULL;
373 }
374
375 struct menu *menu_get_root_menu(struct menu *menu)
376 {
377         return &rootmenu;
378 }
379
380 struct menu *menu_get_parent_menu(struct menu *menu)
381 {
382         enum prop_type type;
383
384         for (; menu != &rootmenu; menu = menu->parent) {
385                 type = menu->prompt ? menu->prompt->type : 0;
386                 if (type == P_MENU)
387                         break;
388         }
389         return menu;
390 }
391
392 struct file *file_lookup(const char *name)
393 {
394         struct file *file;
395
396         for (file = file_list; file; file = file->next) {
397                 if (!strcmp(name, file->name))
398                         return file;
399         }
400
401         file = malloc(sizeof(*file));
402         memset(file, 0, sizeof(*file));
403         file->name = strdup(name);
404         file->next = file_list;
405         file_list = file;
406         return file;
407 }
408
409 int file_write_dep(const char *name)
410 {
411         struct file *file;
412         FILE *out;
413
414         if (!name)
415                 name = ".config.cmd";
416         out = fopen(".config.tmp", "w");
417         if (!out)
418                 return 1;
419         fprintf(out, "deps_config := \\\n");
420         for (file = file_list; file; file = file->next) {
421                 if (file->next)
422                         fprintf(out, "\t%s \\\n", file->name);
423                 else
424                         fprintf(out, "\t%s\n", file->name);
425         }
426         fprintf(out, "\n.config include/config.h: $(deps_config)\n\n$(deps_config):\n");
427         fclose(out);
428         rename(".config.tmp", name);
429         return 0;
430 }
431