summaryrefslogtreecommitdiffstats
path: root/scripts/kconfig/symbol.c
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/kconfig/symbol.c')
-rw-r--r--scripts/kconfig/symbol.c629
1 files changed, 629 insertions, 0 deletions
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
new file mode 100644
index 000000000..845d8a378
--- /dev/null
+++ b/scripts/kconfig/symbol.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+struct symbol symbol_yes = {
+ name: "y",
+ curr: { "y", yes },
+ flags: SYMBOL_YES|SYMBOL_VALID,
+}, symbol_mod = {
+ name: "m",
+ curr: { "m", mod },
+ flags: SYMBOL_MOD|SYMBOL_VALID,
+}, symbol_no = {
+ name: "n",
+ curr: { "n", no },
+ flags: SYMBOL_NO|SYMBOL_VALID,
+}, symbol_empty = {
+ name: "",
+ curr: { "", no },
+ flags: SYMBOL_VALID,
+};
+
+int sym_change_count;
+struct symbol *modules_sym;
+
+void sym_add_default(struct symbol *sym, const char *def)
+{
+ struct property *prop = create_prop(P_DEFAULT);
+ struct property **propp;
+
+ prop->sym = sym;
+ prop->def = sym_lookup(def, 1);
+
+ /* append property to the prop list of symbol */
+ if (prop->sym) {
+ for (propp = &prop->sym->prop; *propp; propp = &(*propp)->next)
+ ;
+ *propp = prop;
+ }
+}
+
+void sym_init(void)
+{
+ struct symbol *sym;
+ struct utsname uts;
+ char *p;
+ static bool inited = false;
+
+ if (inited)
+ return;
+ inited = true;
+
+ uname(&uts);
+
+ sym = sym_lookup("ARCH", 0);
+ sym->type = S_STRING;
+ sym->flags |= SYMBOL_AUTO;
+ p = getenv("ARCH");
+ if (p)
+ sym_add_default(sym, p);
+
+ sym = sym_lookup("KERNELRELEASE", 0);
+ sym->type = S_STRING;
+ sym->flags |= SYMBOL_AUTO;
+ p = getenv("KERNELRELEASE");
+ if (p)
+ sym_add_default(sym, p);
+
+ sym = sym_lookup("UNAME_RELEASE", 0);
+ sym->type = S_STRING;
+ sym->flags |= SYMBOL_AUTO;
+ sym_add_default(sym, uts.release);
+}
+
+int sym_get_type(struct symbol *sym)
+{
+ int type = sym->type;
+ if (type == S_TRISTATE) {
+ if (sym_is_choice_value(sym) && sym->visible == yes)
+ type = S_BOOLEAN;
+ else {
+ sym_calc_value(modules_sym);
+ if (S_TRI(modules_sym->curr) == no)
+ type = S_BOOLEAN;
+ }
+ }
+ return type;
+}
+
+const char *sym_type_name(int type)
+{
+ switch (type) {
+ case S_BOOLEAN:
+ return "boolean";
+ case S_TRISTATE:
+ return "tristate";
+ case S_INT:
+ return "integer";
+ case S_HEX:
+ return "hex";
+ case S_STRING:
+ return "string";
+ case S_UNKNOWN:
+ return "unknown";
+ }
+ return "???";
+}
+
+struct property *sym_get_choice_prop(struct symbol *sym)
+{
+ struct property *prop;
+
+ for_all_choices(sym, prop)
+ return prop;
+ return NULL;
+}
+
+struct property *sym_get_default_prop(struct symbol *sym)
+{
+ struct property *prop;
+ tristate visible;
+
+ for_all_defaults(sym, prop) {
+ visible = E_CALC(prop->visible);
+ if (visible != no)
+ return prop;
+ }
+ return NULL;
+}
+
+void sym_calc_visibility(struct symbol *sym)
+{
+ struct property *prop;
+ tristate visible, oldvisible;
+
+ /* any prompt visible? */
+ oldvisible = sym->visible;
+ visible = no;
+ for_all_prompts(sym, prop)
+ visible = E_OR(visible, E_CALC(prop->visible));
+ if (oldvisible != visible) {
+ sym->visible = visible;
+ sym_set_changed(sym);
+ }
+}
+
+void sym_calc_value(struct symbol *sym)
+{
+ struct symbol_value newval, oldval;
+ struct property *prop, *def_prop;
+ struct symbol *def_sym;
+ struct expr *e;
+
+ if (sym->flags & SYMBOL_VALID)
+ return;
+
+ oldval = sym->curr;
+
+ switch (sym->type) {
+ case S_INT:
+ case S_HEX:
+ case S_STRING:
+ newval = symbol_empty.curr;
+ break;
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ newval = symbol_no.curr;
+ break;
+ default:
+ S_VAL(newval) = sym->name;
+ S_TRI(newval) = no;
+ if (sym->flags & SYMBOL_CONST) {
+ goto out;
+ }
+ //newval = symbol_empty.curr;
+ // generate warning somewhere here later
+ //S_TRI(newval) = yes;
+ goto out;
+ }
+ sym->flags |= SYMBOL_VALID;
+ if (!sym_is_choice_value(sym))
+ sym->flags &= ~SYMBOL_WRITE;
+
+ sym_calc_visibility(sym);
+
+ /* set default if recursively called */
+ sym->curr = newval;
+
+ if (sym->visible != no) {
+ sym->flags |= SYMBOL_WRITE;
+ if (!sym_has_value(sym)) {
+ if (!sym_is_choice(sym)) {
+ prop = sym_get_default_prop(sym);
+ if (prop) {
+ sym_calc_value(prop->def);
+ newval = prop->def->curr;
+ }
+ } else
+ S_TRI(newval) = S_TRI(sym->def);
+ } else
+ newval = sym->def;
+
+ S_TRI(newval) = E_AND(S_TRI(newval), sym->visible);
+ /* if the symbol is visible and not optionial,
+ * possibly ignore old user choice. */
+ if (!sym_is_optional(sym) && S_TRI(newval) == no)
+ S_TRI(newval) = sym->visible;
+ if (sym_is_choice_value(sym) && sym->visible == yes) {
+ prop = sym_get_choice_prop(sym);
+ S_TRI(newval) = (S_VAL(prop->def->curr) == sym) ? yes : no;
+ }
+ } else {
+ prop = sym_get_default_prop(sym);
+ if (prop) {
+ sym->flags |= SYMBOL_WRITE;
+ sym_calc_value(prop->def);
+ newval = prop->def->curr;
+ }
+ }
+
+ switch (sym_get_type(sym)) {
+ case S_TRISTATE:
+ if (S_TRI(newval) != mod)
+ break;
+ sym_calc_value(modules_sym);
+ if (S_TRI(modules_sym->curr) == no)
+ S_TRI(newval) = yes;
+ break;
+ case S_BOOLEAN:
+ if (S_TRI(newval) == mod)
+ S_TRI(newval) = yes;
+ }
+
+out:
+ sym->curr = newval;
+
+ if (sym_is_choice(sym) && S_TRI(newval) == yes) {
+ def_sym = S_VAL(sym->def);
+ if (def_sym) {
+ sym_calc_visibility(def_sym);
+ if (def_sym->visible == no)
+ def_sym = NULL;
+ }
+ if (!def_sym) {
+ for_all_defaults(sym, def_prop) {
+ if (E_CALC(def_prop->visible) == no)
+ continue;
+ sym_calc_visibility(def_prop->def);
+ if (def_prop->def->visible != no) {
+ def_sym = def_prop->def;
+ break;
+ }
+ }
+ }
+
+ if (!def_sym) {
+ prop = sym_get_choice_prop(sym);
+ for (e = prop->dep; e; e = e->left.expr) {
+ sym_calc_visibility(e->right.sym);
+ if (e->right.sym->visible != no) {
+ def_sym = e->right.sym;
+ break;
+ }
+ }
+ }
+
+ S_VAL(newval) = def_sym;
+ }
+
+ if (memcmp(&oldval, &newval, sizeof(newval)))
+ sym_set_changed(sym);
+ sym->curr = newval;
+
+ if (sym_is_choice(sym)) {
+ int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE);
+ prop = sym_get_choice_prop(sym);
+ for (e = prop->dep; e; e = e->left.expr) {
+ e->right.sym->flags |= flags;
+ if (flags & SYMBOL_CHANGED)
+ sym_set_changed(e->right.sym);
+ }
+ }
+}
+
+void sym_clear_all_valid(void)
+{
+ struct symbol *sym;
+ int i;
+
+ for_all_symbols(i, sym)
+ sym->flags &= ~SYMBOL_VALID;
+ sym_change_count++;
+}
+
+void sym_set_changed(struct symbol *sym)
+{
+ struct property *prop;
+
+ sym->flags |= SYMBOL_CHANGED;
+ for (prop = sym->prop; prop; prop = prop->next) {
+ if (prop->menu)
+ prop->menu->flags |= MENU_CHANGED;
+ }
+}
+
+void sym_set_all_changed(void)
+{
+ struct symbol *sym;
+ int i;
+
+ for_all_symbols(i, sym)
+ sym_set_changed(sym);
+}
+
+bool sym_tristate_within_range(struct symbol *sym, tristate val)
+{
+ int type = sym_get_type(sym);
+
+ if (sym->visible == no)
+ return false;
+
+ if (type != S_BOOLEAN && type != S_TRISTATE)
+ return false;
+
+ switch (val) {
+ case no:
+ if (sym_is_choice_value(sym) && sym->visible == yes)
+ return false;
+ return sym_is_optional(sym);
+ case mod:
+ if (sym_is_choice_value(sym) && sym->visible == yes)
+ return false;
+ return type == S_TRISTATE;
+ case yes:
+ return type == S_BOOLEAN || sym->visible == yes;
+ }
+ return false;
+}
+
+bool sym_set_tristate_value(struct symbol *sym, tristate val)
+{
+ tristate oldval = sym_get_tristate_value(sym);
+
+ if (oldval != val && !sym_tristate_within_range(sym, val))
+ return false;
+
+ if (sym->flags & SYMBOL_NEW) {
+ sym->flags &= ~SYMBOL_NEW;
+ sym_set_changed(sym);
+ }
+ if (sym_is_choice_value(sym) && val == yes) {
+ struct property *prop = sym_get_choice_prop(sym);
+
+ S_VAL(prop->def->def) = sym;
+ prop->def->flags &= ~SYMBOL_NEW;
+ }
+
+ S_TRI(sym->def) = val;
+ if (oldval != val) {
+ sym_clear_all_valid();
+ if (sym == modules_sym)
+ sym_set_all_changed();
+ }
+
+ return true;
+}
+
+tristate sym_toggle_tristate_value(struct symbol *sym)
+{
+ tristate oldval, newval;
+
+ oldval = newval = sym_get_tristate_value(sym);
+ do {
+ switch (newval) {
+ case no:
+ newval = mod;
+ break;
+ case mod:
+ newval = yes;
+ break;
+ case yes:
+ newval = no;
+ break;
+ }
+ if (sym_set_tristate_value(sym, newval))
+ break;
+ } while (oldval != newval);
+ return newval;
+}
+
+bool sym_string_valid(struct symbol *sym, const char *str)
+{
+ char ch;
+
+ switch (sym->type) {
+ case S_STRING:
+ return true;
+ case S_INT:
+ ch = *str++;
+ if (ch == '-')
+ ch = *str++;
+ if (!isdigit(ch))
+ return false;
+ if (ch == '0' && *str != 0)
+ return false;
+ while ((ch = *str++)) {
+ if (!isdigit(ch))
+ return false;
+ }
+ return true;
+ case S_HEX:
+ if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+ str += 2;
+ ch = *str++;
+ do {
+ if (!isxdigit(ch))
+ return false;
+ } while ((ch = *str++));
+ return true;
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (str[0]) {
+ case 'y':
+ case 'Y':
+ return sym_tristate_within_range(sym, yes);
+ case 'm':
+ case 'M':
+ return sym_tristate_within_range(sym, mod);
+ case 'n':
+ case 'N':
+ return sym_tristate_within_range(sym, no);
+ }
+ return false;
+ default:
+ return false;
+ }
+}
+
+bool sym_set_string_value(struct symbol *sym, const char *newval)
+{
+ const char *oldval;
+ char *val;
+ int size;
+
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ switch (newval[0]) {
+ case 'y':
+ case 'Y':
+ return sym_set_tristate_value(sym, yes);
+ case 'm':
+ case 'M':
+ return sym_set_tristate_value(sym, mod);
+ case 'n':
+ case 'N':
+ return sym_set_tristate_value(sym, no);
+ }
+ return false;
+ default:
+ ;
+ }
+
+ if (!sym_string_valid(sym, newval))
+ return false;
+
+ if (sym->flags & SYMBOL_NEW) {
+ sym->flags &= ~SYMBOL_NEW;
+ sym_set_changed(sym);
+ }
+
+ oldval = S_VAL(sym->def);
+ size = strlen(newval) + 1;
+ if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
+ size += 2;
+ S_VAL(sym->def) = val = malloc(size);
+ *val++ = '0';
+ *val++ = 'x';
+ } else if (!oldval || strcmp(oldval, newval))
+ S_VAL(sym->def) = val = malloc(size);
+ else
+ return true;
+
+ strcpy(val, newval);
+ free((void *)oldval);
+ sym_clear_all_valid();
+
+ return true;
+}
+
+const char *sym_get_string_value(struct symbol *sym)
+{
+ tristate val;
+
+ switch (sym->type) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ val = sym_get_tristate_value(sym);
+ switch (val) {
+ case no:
+ return "n";
+ case mod:
+ return "m";
+ case yes:
+ return "y";
+ }
+ break;
+ default:
+ ;
+ }
+ return (const char *)S_VAL(sym->curr);
+}
+
+bool sym_is_changable(struct symbol *sym)
+{
+ if (sym->visible == no)
+ return false;
+ /* at least 'n' and 'y'/'m' is selectable */
+ if (sym_is_optional(sym))
+ return true;
+ /* no 'n', so 'y' and 'm' must be selectable */
+ if (sym_get_type(sym) == S_TRISTATE && sym->visible == yes)
+ return true;
+ return false;
+}
+
+struct symbol *sym_lookup(const char *name, int isconst)
+{
+ struct symbol *symbol;
+ const char *ptr;
+ char *new_name;
+ int hash = 0;
+
+ if (name) {
+ if (name[0] && !name[1]) {
+ switch (name[0]) {
+ case 'y': return &symbol_yes;
+ case 'm': return &symbol_mod;
+ case 'n': return &symbol_no;
+ }
+ }
+ for (ptr = name; *ptr; ptr++)
+ hash += *ptr;
+ hash &= 0xff;
+
+ for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+ if (!strcmp(symbol->name, name)) {
+ if ((isconst && symbol->flags & SYMBOL_CONST) ||
+ (!isconst && !(symbol->flags & SYMBOL_CONST)))
+ return symbol;
+ }
+ }
+ new_name = strdup(name);
+ } else {
+ new_name = NULL;
+ hash = 256;
+ }
+
+ symbol = malloc(sizeof(*symbol));
+ memset(symbol, 0, sizeof(*symbol));
+ symbol->name = new_name;
+ symbol->type = S_UNKNOWN;
+ symbol->flags = SYMBOL_NEW;
+ if (isconst)
+ symbol->flags |= SYMBOL_CONST;
+
+ symbol->next = symbol_hash[hash];
+ symbol_hash[hash] = symbol;
+
+ return symbol;
+}
+
+struct symbol *sym_find(const char *name)
+{
+ struct symbol *symbol = NULL;
+ const char *ptr;
+ int hash = 0;
+
+ if (!name)
+ return NULL;
+
+ if (name[0] && !name[1]) {
+ switch (name[0]) {
+ case 'y': return &symbol_yes;
+ case 'm': return &symbol_mod;
+ case 'n': return &symbol_no;
+ }
+ }
+ for (ptr = name; *ptr; ptr++)
+ hash += *ptr;
+ hash &= 0xff;
+
+ for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+ if (!strcmp(symbol->name, name) &&
+ !(symbol->flags & SYMBOL_CONST))
+ break;
+ }
+
+ return symbol;
+}
+
+const char *prop_get_type_name(enum prop_type type)
+{
+ switch (type) {
+ case P_PROMPT:
+ return "prompt";
+ case P_COMMENT:
+ return "comment";
+ case P_MENU:
+ return "menu";
+ case P_ROOTMENU:
+ return "rootmenu";
+ case P_DEFAULT:
+ return "default";
+ case P_CHOICE:
+ return "choice";
+ default:
+ return "unknown";
+ }
+}