-rw-r--r-- | scripts/kconfig/symbol.c | 618 |
1 files changed, 618 insertions, 0 deletions
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c new file mode 100644 index 0000000..59c88d2 --- a/dev/null +++ b/scripts/kconfig/symbol.c | |||
@@ -0,0 +1,618 @@ | |||
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 <ctype.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <string.h> | ||
9 | #include <sys/utsname.h> | ||
10 | |||
11 | #define LKC_DIRECT_LINK | ||
12 | #include "lkc.h" | ||
13 | |||
14 | struct symbol symbol_yes = { | ||
15 | name: "y", | ||
16 | curr: { "y", yes }, | ||
17 | flags: SYMBOL_YES|SYMBOL_VALID, | ||
18 | }, symbol_mod = { | ||
19 | name: "m", | ||
20 | curr: { "m", mod }, | ||
21 | flags: SYMBOL_MOD|SYMBOL_VALID, | ||
22 | }, symbol_no = { | ||
23 | name: "n", | ||
24 | curr: { "n", no }, | ||
25 | flags: SYMBOL_NO|SYMBOL_VALID, | ||
26 | }, symbol_empty = { | ||
27 | name: "", | ||
28 | curr: { "", no }, | ||
29 | flags: SYMBOL_VALID, | ||
30 | }; | ||
31 | |||
32 | int sym_change_count; | ||
33 | struct symbol *modules_sym; | ||
34 | |||
35 | void sym_add_default(struct symbol *sym, const char *def) | ||
36 | { | ||
37 | struct property *prop = create_prop(P_DEFAULT); | ||
38 | struct property **propp; | ||
39 | |||
40 | prop->sym = sym; | ||
41 | prop->def = sym_lookup(def, 1); | ||
42 | |||
43 | /* append property to the prop list of symbol */ | ||
44 | if (prop->sym) { | ||
45 | for (propp = &prop->sym->prop; *propp; propp = &(*propp)->next) | ||
46 | ; | ||
47 | *propp = prop; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | void sym_init(void) | ||
52 | { | ||
53 | struct symbol *sym; | ||
54 | struct utsname uts; | ||
55 | char *p; | ||
56 | static bool inited = false; | ||
57 | |||
58 | if (inited) | ||
59 | return; | ||
60 | inited = true; | ||
61 | |||
62 | uname(&uts); | ||
63 | |||
64 | sym = sym_lookup("ARCH", 0); | ||
65 | sym->type = S_STRING; | ||
66 | sym->flags |= SYMBOL_AUTO; | ||
67 | p = getenv("ARCH"); | ||
68 | if (p) | ||
69 | sym_add_default(sym, p); | ||
70 | |||
71 | sym = sym_lookup("KERNELRELEASE", 0); | ||
72 | sym->type = S_STRING; | ||
73 | sym->flags |= SYMBOL_AUTO; | ||
74 | p = getenv("KERNELRELEASE"); | ||
75 | if (p) | ||
76 | sym_add_default(sym, p); | ||
77 | |||
78 | sym = sym_lookup("UNAME_RELEASE", 0); | ||
79 | sym->type = S_STRING; | ||
80 | sym->flags |= SYMBOL_AUTO; | ||
81 | sym_add_default(sym, uts.release); | ||
82 | } | ||
83 | |||
84 | int sym_get_type(struct symbol *sym) | ||
85 | { | ||
86 | int type = sym->type; | ||
87 | if (type == S_TRISTATE) { | ||
88 | if (sym_is_choice_value(sym) && sym->visible == yes) | ||
89 | type = S_BOOLEAN; | ||
90 | else { | ||
91 | sym_calc_value(modules_sym); | ||
92 | if (S_TRI(modules_sym->curr) == no) | ||
93 | type = S_BOOLEAN; | ||
94 | } | ||
95 | } | ||
96 | return type; | ||
97 | } | ||
98 | |||
99 | const char *sym_type_name(int type) | ||
100 | { | ||
101 | switch (type) { | ||
102 | case S_BOOLEAN: | ||
103 | return "boolean"; | ||
104 | case S_TRISTATE: | ||
105 | return "tristate"; | ||
106 | case S_INT: | ||
107 | return "integer"; | ||
108 | case S_HEX: | ||
109 | return "hex"; | ||
110 | case S_STRING: | ||
111 | return "string"; | ||
112 | case S_UNKNOWN: | ||
113 | return "unknown"; | ||
114 | } | ||
115 | return "???"; | ||
116 | } | ||
117 | |||
118 | struct property *sym_get_choice_prop(struct symbol *sym) | ||
119 | { | ||
120 | struct property *prop; | ||
121 | |||
122 | for_all_choices(sym, prop) | ||
123 | return prop; | ||
124 | return NULL; | ||
125 | } | ||
126 | |||
127 | struct property *sym_get_default_prop(struct symbol *sym) | ||
128 | { | ||
129 | struct property *prop; | ||
130 | tristate visible; | ||
131 | |||
132 | for_all_defaults(sym, prop) { | ||
133 | visible = E_CALC(prop->visible); | ||
134 | if (visible != no) | ||
135 | return prop; | ||
136 | } | ||
137 | return NULL; | ||
138 | } | ||
139 | |||
140 | void sym_calc_visibility(struct symbol *sym) | ||
141 | { | ||
142 | struct property *prop; | ||
143 | tristate visible, oldvisible; | ||
144 | |||
145 | /* any prompt visible? */ | ||
146 | oldvisible = sym->visible; | ||
147 | visible = no; | ||
148 | for_all_prompts(sym, prop) | ||
149 | visible = E_OR(visible, E_CALC(prop->visible)); | ||
150 | if (oldvisible != visible) { | ||
151 | sym->visible = visible; | ||
152 | sym->flags |= SYMBOL_CHANGED; | ||
153 | } | ||
154 | } | ||
155 | |||
156 | void sym_calc_value(struct symbol *sym) | ||
157 | { | ||
158 | struct symbol_value newval, oldval; | ||
159 | struct property *prop, *def_prop; | ||
160 | struct symbol *def_sym; | ||
161 | struct expr *e; | ||
162 | |||
163 | if (sym->flags & SYMBOL_VALID) | ||
164 | return; | ||
165 | |||
166 | oldval = sym->curr; | ||
167 | |||
168 | switch (sym->type) { | ||
169 | case S_INT: | ||
170 | case S_HEX: | ||
171 | case S_STRING: | ||
172 | newval = symbol_empty.curr; | ||
173 | break; | ||
174 | case S_BOOLEAN: | ||
175 | case S_TRISTATE: | ||
176 | newval = symbol_no.curr; | ||
177 | break; | ||
178 | default: | ||
179 | S_VAL(newval) = sym->name; | ||
180 | S_TRI(newval) = no; | ||
181 | if (sym->flags & SYMBOL_CONST) { | ||
182 | goto out; | ||
183 | } | ||
184 | //newval = symbol_empty.curr; | ||
185 | // generate warning somewhere here later | ||
186 | //S_TRI(newval) = yes; | ||
187 | goto out; | ||
188 | } | ||
189 | sym->flags |= SYMBOL_VALID; | ||
190 | if (!sym_is_choice_value(sym)) | ||
191 | sym->flags &= ~SYMBOL_WRITE; | ||
192 | |||
193 | sym_calc_visibility(sym); | ||
194 | |||
195 | /* set default if recursively called */ | ||
196 | sym->curr = newval; | ||
197 | |||
198 | if (sym->visible != no) { | ||
199 | sym->flags |= SYMBOL_WRITE; | ||
200 | if (!sym_has_value(sym)) { | ||
201 | if (!sym_is_choice(sym)) { | ||
202 | prop = sym_get_default_prop(sym); | ||
203 | if (prop) { | ||
204 | sym_calc_value(prop->def); | ||
205 | newval = prop->def->curr; | ||
206 | } | ||
207 | } | ||
208 | } else | ||
209 | newval = sym->def; | ||
210 | |||
211 | S_TRI(newval) = E_AND(S_TRI(newval), sym->visible); | ||
212 | /* if the symbol is visible and not optionial, | ||
213 | * possibly ignore old user choice. */ | ||
214 | if (!sym_is_optional(sym) && S_TRI(newval) == no) | ||
215 | S_TRI(newval) = sym->visible; | ||
216 | if (sym_is_choice_value(sym) && sym->visible == yes) { | ||
217 | prop = sym_get_choice_prop(sym); | ||
218 | S_TRI(newval) = (S_VAL(prop->def->curr) == sym) ? yes : no; | ||
219 | } | ||
220 | } else { | ||
221 | prop = sym_get_default_prop(sym); | ||
222 | if (prop) { | ||
223 | sym->flags |= SYMBOL_WRITE; | ||
224 | sym_calc_value(prop->def); | ||
225 | newval = prop->def->curr; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | switch (sym_get_type(sym)) { | ||
230 | case S_TRISTATE: | ||
231 | if (S_TRI(newval) != mod) | ||
232 | break; | ||
233 | sym_calc_value(modules_sym); | ||
234 | if (S_TRI(modules_sym->curr) == no) | ||
235 | S_TRI(newval) = yes; | ||
236 | break; | ||
237 | case S_BOOLEAN: | ||
238 | if (S_TRI(newval) == mod) | ||
239 | S_TRI(newval) = yes; | ||
240 | } | ||
241 | |||
242 | out: | ||
243 | sym->curr = newval; | ||
244 | |||
245 | if (sym_is_choice(sym) && S_TRI(newval) == yes) { | ||
246 | def_sym = S_VAL(sym->def); | ||
247 | if (def_sym) { | ||
248 | sym_calc_visibility(def_sym); | ||
249 | if (def_sym->visible == no) | ||
250 | def_sym = NULL; | ||
251 | } | ||
252 | if (!def_sym) { | ||
253 | for_all_defaults(sym, def_prop) { | ||
254 | if (E_CALC(def_prop->visible) == no) | ||
255 | continue; | ||
256 | sym_calc_visibility(def_prop->def); | ||
257 | if (def_prop->def->visible != no) { | ||
258 | def_sym = def_prop->def; | ||
259 | break; | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | |||
264 | if (!def_sym) { | ||
265 | prop = sym_get_choice_prop(sym); | ||
266 | for (e = prop->dep; e; e = e->left.expr) { | ||
267 | sym_calc_visibility(e->right.sym); | ||
268 | if (e->right.sym->visible != no) { | ||
269 | def_sym = e->right.sym; | ||
270 | break; | ||
271 | } | ||
272 | } | ||
273 | } | ||
274 | |||
275 | S_VAL(newval) = def_sym; | ||
276 | } | ||
277 | |||
278 | if (memcmp(&oldval, &newval, sizeof(newval))) | ||
279 | sym->flags |= SYMBOL_CHANGED; | ||
280 | sym->curr = newval; | ||
281 | |||
282 | if (sym_is_choice(sym)) { | ||
283 | int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE); | ||
284 | prop = sym_get_choice_prop(sym); | ||
285 | for (e = prop->dep; e; e = e->left.expr) | ||
286 | e->right.sym->flags |= flags; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | void sym_clear_all_valid(void) | ||
291 | { | ||
292 | struct symbol *sym; | ||
293 | int i; | ||
294 | |||
295 | for_all_symbols(i, sym) | ||
296 | sym->flags &= ~SYMBOL_VALID; | ||
297 | sym_change_count++; | ||
298 | } | ||
299 | |||
300 | void sym_set_all_changed(void) | ||
301 | { | ||
302 | struct symbol *sym; | ||
303 | int i; | ||
304 | |||
305 | for_all_symbols(i, sym) | ||
306 | sym->flags |= SYMBOL_CHANGED; | ||
307 | } | ||
308 | |||
309 | bool sym_tristate_within_range(struct symbol *sym, tristate val) | ||
310 | { | ||
311 | int type = sym_get_type(sym); | ||
312 | |||
313 | if (sym->visible == no) | ||
314 | return false; | ||
315 | |||
316 | if (type != S_BOOLEAN && type != S_TRISTATE) | ||
317 | return false; | ||
318 | |||
319 | switch (val) { | ||
320 | case no: | ||
321 | if (sym_is_choice_value(sym) && sym->visible == yes) | ||
322 | return false; | ||
323 | return sym_is_optional(sym); | ||
324 | case mod: | ||
325 | if (sym_is_choice_value(sym) && sym->visible == yes) | ||
326 | return false; | ||
327 | return type == S_TRISTATE; | ||
328 | case yes: | ||
329 | return type == S_BOOLEAN || sym->visible == yes; | ||
330 | } | ||
331 | return false; | ||
332 | } | ||
333 | |||
334 | bool sym_set_tristate_value(struct symbol *sym, tristate val) | ||
335 | { | ||
336 | tristate oldval = sym_get_tristate_value(sym); | ||
337 | |||
338 | if (oldval != val && !sym_tristate_within_range(sym, val)) | ||
339 | return false; | ||
340 | |||
341 | if (sym->flags & SYMBOL_NEW) { | ||
342 | sym->flags &= ~SYMBOL_NEW; | ||
343 | sym->flags |= SYMBOL_CHANGED; | ||
344 | } | ||
345 | if (sym_is_choice_value(sym) && val == yes) { | ||
346 | struct property *prop = sym_get_choice_prop(sym); | ||
347 | |||
348 | S_VAL(prop->def->def) = sym; | ||
349 | prop->def->flags &= ~SYMBOL_NEW; | ||
350 | } | ||
351 | |||
352 | S_TRI(sym->def) = val; | ||
353 | if (oldval != val) { | ||
354 | sym_clear_all_valid(); | ||
355 | if (sym == modules_sym) | ||
356 | sym_set_all_changed(); | ||
357 | } | ||
358 | |||
359 | return true; | ||
360 | } | ||
361 | |||
362 | tristate sym_toggle_tristate_value(struct symbol *sym) | ||
363 | { | ||
364 | tristate oldval, newval; | ||
365 | |||
366 | oldval = newval = sym_get_tristate_value(sym); | ||
367 | do { | ||
368 | switch (newval) { | ||
369 | case no: | ||
370 | newval = mod; | ||
371 | break; | ||
372 | case mod: | ||
373 | newval = yes; | ||
374 | break; | ||
375 | case yes: | ||
376 | newval = no; | ||
377 | break; | ||
378 | } | ||
379 | if (sym_set_tristate_value(sym, newval)) | ||
380 | break; | ||
381 | } while (oldval != newval); | ||
382 | return newval; | ||
383 | } | ||
384 | |||
385 | bool sym_string_valid(struct symbol *sym, const char *str) | ||
386 | { | ||
387 | char ch; | ||
388 | |||
389 | switch (sym->type) { | ||
390 | case S_STRING: | ||
391 | return true; | ||
392 | case S_INT: | ||
393 | ch = *str++; | ||
394 | if (ch == '-') | ||
395 | ch = *str++; | ||
396 | if (!isdigit(ch)) | ||
397 | return false; | ||
398 | if (ch == '0' && *str != 0) | ||
399 | return false; | ||
400 | while ((ch = *str++)) { | ||
401 | if (!isdigit(ch)) | ||
402 | return false; | ||
403 | } | ||
404 | return true; | ||
405 | case S_HEX: | ||
406 | if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) | ||
407 | str += 2; | ||
408 | ch = *str++; | ||
409 | do { | ||
410 | if (!isxdigit(ch)) | ||
411 | return false; | ||
412 | } while ((ch = *str++)); | ||
413 | return true; | ||
414 | case S_BOOLEAN: | ||
415 | case S_TRISTATE: | ||
416 | switch (str[0]) { | ||
417 | case 'y': | ||
418 | case 'Y': | ||
419 | return sym_tristate_within_range(sym, yes); | ||
420 | case 'm': | ||
421 | case 'M': | ||
422 | return sym_tristate_within_range(sym, mod); | ||
423 | case 'n': | ||
424 | case 'N': | ||
425 | return sym_tristate_within_range(sym, no); | ||
426 | } | ||
427 | return false; | ||
428 | default: | ||
429 | return false; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | bool sym_set_string_value(struct symbol *sym, const char *newval) | ||
434 | { | ||
435 | const char *oldval; | ||
436 | char *val; | ||
437 | int size; | ||
438 | |||
439 | switch (sym->type) { | ||
440 | case S_BOOLEAN: | ||
441 | case S_TRISTATE: | ||
442 | switch (newval[0]) { | ||
443 | case 'y': | ||
444 | case 'Y': | ||
445 | return sym_set_tristate_value(sym, yes); | ||
446 | case 'm': | ||
447 | case 'M': | ||
448 | return sym_set_tristate_value(sym, mod); | ||
449 | case 'n': | ||
450 | case 'N': | ||
451 | return sym_set_tristate_value(sym, no); | ||
452 | } | ||
453 | return false; | ||
454 | default: | ||
455 | ; | ||
456 | } | ||
457 | |||
458 | if (!sym_string_valid(sym, newval)) | ||
459 | return false; | ||
460 | |||
461 | if (sym->flags & SYMBOL_NEW) { | ||
462 | sym->flags &= ~SYMBOL_NEW; | ||
463 | sym->flags |= SYMBOL_CHANGED; | ||
464 | } | ||
465 | |||
466 | oldval = S_VAL(sym->def); | ||
467 | size = strlen(newval) + 1; | ||
468 | if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { | ||
469 | size += 2; | ||
470 | S_VAL(sym->def) = val = malloc(size); | ||
471 | *val++ = '0'; | ||
472 | *val++ = 'x'; | ||
473 | } else if (!oldval || strcmp(oldval, newval)) | ||
474 | S_VAL(sym->def) = val = malloc(size); | ||
475 | else | ||
476 | return true; | ||
477 | |||
478 | strcpy(val, newval); | ||
479 | free((void *)oldval); | ||
480 | sym_clear_all_valid(); | ||
481 | |||
482 | return true; | ||
483 | } | ||
484 | |||
485 | const char *sym_get_string_value(struct symbol *sym) | ||
486 | { | ||
487 | tristate val; | ||
488 | |||
489 | switch (sym->type) { | ||
490 | case S_BOOLEAN: | ||
491 | case S_TRISTATE: | ||
492 | val = sym_get_tristate_value(sym); | ||
493 | switch (val) { | ||
494 | case no: | ||
495 | return "n"; | ||
496 | case mod: | ||
497 | return "m"; | ||
498 | case yes: | ||
499 | return "y"; | ||
500 | } | ||
501 | break; | ||
502 | default: | ||
503 | ; | ||
504 | } | ||
505 | return (const char *)S_VAL(sym->curr); | ||
506 | } | ||
507 | |||
508 | bool sym_is_changable(struct symbol *sym) | ||
509 | { | ||
510 | if (sym->visible == no) | ||
511 | return false; | ||
512 | /* at least 'n' and 'y'/'m' is selectable */ | ||
513 | if (sym_is_optional(sym)) | ||
514 | return true; | ||
515 | /* no 'n', so 'y' and 'm' must be selectable */ | ||
516 | if (sym_get_type(sym) == S_TRISTATE && sym->visible == yes) | ||
517 | return true; | ||
518 | return false; | ||
519 | } | ||
520 | |||
521 | struct symbol *sym_lookup(const char *name, int isconst) | ||
522 | { | ||
523 | struct symbol *symbol; | ||
524 | const char *ptr; | ||
525 | char *new_name; | ||
526 | int hash = 0; | ||
527 | |||
528 | //printf("lookup: %s -> ", name); | ||
529 | if (name) { | ||
530 | if (name[0] && !name[1]) { | ||
531 | switch (name[0]) { | ||
532 | case 'y': return &symbol_yes; | ||
533 | case 'm': return &symbol_mod; | ||
534 | case 'n': return &symbol_no; | ||
535 | } | ||
536 | } | ||
537 | for (ptr = name; *ptr; ptr++) | ||
538 | hash += *ptr; | ||
539 | hash &= 0xff; | ||
540 | |||
541 | for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { | ||
542 | if (!strcmp(symbol->name, name)) { | ||
543 | if ((isconst && symbol->flags & SYMBOL_CONST) || | ||
544 | (!isconst && !(symbol->flags & SYMBOL_CONST))) { | ||
545 | //printf("h:%p\n", symbol); | ||
546 | return symbol; | ||
547 | } | ||
548 | } | ||
549 | } | ||
550 | new_name = strdup(name); | ||
551 | } else { | ||
552 | new_name = NULL; | ||
553 | hash = 256; | ||
554 | } | ||
555 | |||
556 | symbol = malloc(sizeof(*symbol)); | ||
557 | memset(symbol, 0, sizeof(*symbol)); | ||
558 | symbol->name = new_name; | ||
559 | symbol->type = S_UNKNOWN; | ||
560 | symbol->flags = SYMBOL_NEW; | ||
561 | if (isconst) | ||
562 | symbol->flags |= SYMBOL_CONST; | ||
563 | |||
564 | symbol->next = symbol_hash[hash]; | ||
565 | symbol_hash[hash] = symbol; | ||
566 | |||
567 | //printf("n:%p\n", symbol); | ||
568 | return symbol; | ||
569 | } | ||
570 | |||
571 | struct symbol *sym_find(const char *name) | ||
572 | { | ||
573 | struct symbol *symbol = NULL; | ||
574 | const char *ptr; | ||
575 | int hash = 0; | ||
576 | |||
577 | if (!name) | ||
578 | return NULL; | ||
579 | |||
580 | if (name[0] && !name[1]) { | ||
581 | switch (name[0]) { | ||
582 | case 'y': return &symbol_yes; | ||
583 | case 'm': return &symbol_mod; | ||
584 | case 'n': return &symbol_no; | ||
585 | } | ||
586 | } | ||
587 | for (ptr = name; *ptr; ptr++) | ||
588 | hash += *ptr; | ||
589 | hash &= 0xff; | ||
590 | |||
591 | for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) { | ||
592 | if (!strcmp(symbol->name, name) && | ||
593 | !(symbol->flags & SYMBOL_CONST)) | ||
594 | break; | ||
595 | } | ||
596 | |||
597 | return symbol; | ||
598 | } | ||
599 | |||
600 | const char *prop_get_type_name(enum prop_type type) | ||
601 | { | ||
602 | switch (type) { | ||
603 | case P_PROMPT: | ||
604 | return "prompt"; | ||
605 | case P_COMMENT: | ||
606 | return "comment"; | ||
607 | case P_MENU: | ||
608 | return "menu"; | ||
609 | case P_ROOTMENU: | ||
610 | return "rootmenu"; | ||
611 | case P_DEFAULT: | ||
612 | return "default"; | ||
613 | case P_CHOICE: | ||
614 | return "choice"; | ||
615 | default: | ||
616 | return "unknown"; | ||
617 | } | ||
618 | } | ||