return name;
}
+static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
+{
+ char *p2;
+
+ switch (sym->type) {
+ case S_TRISTATE:
+ if (p[0] == 'm') {
+ sym->def[def].tri = mod;
+ sym->flags |= def_flags;
+ break;
+ }
+ case S_BOOLEAN:
+ if (p[0] == 'y') {
+ sym->def[def].tri = yes;
+ sym->flags |= def_flags;
+ break;
+ }
+ if (p[0] == 'n') {
+ sym->def[def].tri = no;
+ sym->flags |= def_flags;
+ break;
+ }
+ conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+ break;
+ case S_OTHER:
+ if (*p != '"') {
+ for (p2 = p; *p2 && !isspace(*p2); p2++)
+ ;
+ sym->type = S_STRING;
+ goto done;
+ }
+ case S_STRING:
+ if (*p++ != '"')
+ break;
+ for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+ if (*p2 == '"') {
+ *p2 = 0;
+ break;
+ }
+ memmove(p2, p2 + 1, strlen(p2));
+ }
+ if (!p2) {
+ conf_warning("invalid string found");
+ return 1;
+ }
+ case S_INT:
+ case S_HEX:
+ done:
+ if (sym_string_valid(sym, p)) {
+ sym->def[def].val = strdup(p);
+ sym->flags |= def_flags;
+ } else {
+ conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+ return 1;
+ }
+ break;
+ default:
+ ;
+ }
+ return 0;
+}
+
+/* Read an environment variable and assign the value to the symbol */
+int conf_set_env_sym(const char *env, const char *symname, int def)
+{
+ struct symbol *sym;
+ char *p;
+ int def_flags;
+
+ p = getenv(env);
+ if (p) {
+ char warning[200];
+ sprintf(warning, "Environment variable (%s = \"%s\")", env, p);
+ conf_filename = warning;
+ def_flags = SYMBOL_DEF << def;
+ if (def == S_DEF_USER) {
+ sym = sym_find(symname);
+ if (!sym)
+ return 1;
+ } else {
+ sym = sym_lookup(symname, 0);
+ if (sym->type == S_UNKNOWN)
+ sym->type = S_OTHER;
+ }
+ conf_set_sym_val(sym, def, def_flags, p);
+ }
+ return 0;
+}
+
int conf_read_simple(const char *name, int def)
{
FILE *in = NULL;
in = zconf_fopen(name);
if (in)
goto load;
- sym_change_count++;
+ sym_add_change_count(1);
if (!sym_defconfig_list)
return 1;
continue;
*p++ = 0;
p2 = strchr(p, '\n');
- if (p2)
- *p2 = 0;
+ if (p2) {
+ *p2-- = 0;
+ if (*p2 == '\r')
+ *p2 = 0;
+ }
if (def == S_DEF_USER) {
sym = sym_find(line + 7);
if (!sym) {
conf_warning("trying to reassign symbol %s", sym->name);
break;
}
- switch (sym->type) {
- case S_TRISTATE:
- if (p[0] == 'm') {
- sym->def[def].tri = mod;
- sym->flags |= def_flags;
- break;
- }
- case S_BOOLEAN:
- if (p[0] == 'y') {
- sym->def[def].tri = yes;
- sym->flags |= def_flags;
- break;
- }
- if (p[0] == 'n') {
- sym->def[def].tri = no;
- sym->flags |= def_flags;
- break;
- }
- conf_warning("symbol value '%s' invalid for %s", p, sym->name);
- break;
- case S_OTHER:
- if (*p != '"') {
- for (p2 = p; *p2 && !isspace(*p2); p2++)
- ;
- sym->type = S_STRING;
- goto done;
- }
- case S_STRING:
- if (*p++ != '"')
- break;
- for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
- if (*p2 == '"') {
- *p2 = 0;
- break;
- }
- memmove(p2, p2 + 1, strlen(p2));
- }
- if (!p2) {
- conf_warning("invalid string found");
- continue;
- }
- case S_INT:
- case S_HEX:
- done:
- if (sym_string_valid(sym, p)) {
- sym->def[def].val = strdup(p);
- sym->flags |= def_flags;
- } else {
- conf_warning("symbol value '%s' invalid for %s", p, sym->name);
- continue;
- }
- break;
- default:
- ;
- }
+ if (conf_set_sym_val(sym, def, def_flags, p))
+ continue;
break;
+ case '\r':
case '\n':
break;
default:
struct expr *e;
int i, flags;
- sym_change_count = 0;
+ sym_set_change_count(0);
if (conf_read_simple(name, S_DEF_USER))
return 1;
conf_unsaved++;
/* maybe print value in verbose mode... */
sym_ok:
+ if (!sym_is_choice(sym))
+ continue;
+ /* The choice symbol only has a set value (and thus is not new)
+ * if all its visible childs have values.
+ */
+ prop = sym_get_choice_prop(sym);
+ flags = sym->flags;
+ for (e = prop->expr; e; e = e->left.expr)
+ if (e->right.sym->visible != no)
+ flags &= e->right.sym->flags;
+ sym->flags &= flags | ~SYMBOL_DEF_USER;
+ }
+
+ for_all_symbols(i, sym) {
if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
- if (sym->visible == no)
+ /* Reset values of generates values, so they'll appear
+ * as new, if they should become visible, but that
+ * doesn't quite work if the Kconfig and the saved
+ * configuration disagree.
+ */
+ if (sym->visible == no && !conf_unsaved)
sym->flags &= ~SYMBOL_DEF_USER;
switch (sym->type) {
case S_STRING:
case S_INT:
case S_HEX:
- if (!sym_string_within_range(sym, sym->def[S_DEF_USER].val))
- sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER);
+ /* Reset a string value if it's out of range */
+ if (sym_string_within_range(sym, sym->def[S_DEF_USER].val))
+ break;
+ sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER);
+ conf_unsaved++;
+ break;
default:
break;
}
}
- if (!sym_is_choice(sym))
- continue;
- prop = sym_get_choice_prop(sym);
- flags = sym->flags;
- for (e = prop->expr; e; e = e->left.expr)
- if (e->right.sym->visible != no)
- flags &= e->right.sym->flags;
- sym->flags &= flags | ~SYMBOL_DEF_USER;
}
- sym_change_count += conf_warnings || conf_unsaved;
+ sym_add_change_count(conf_warnings || conf_unsaved);
return 0;
}
use_timestamp ? "# " : "",
use_timestamp ? ctime(&now) : "");
- if (!sym_change_count)
+ if (!conf_get_changed())
sym_clear_all_valid();
menu = rootmenu.list;
fclose(out);
if (*tmpname) {
- strcat(dirname, name ? name : conf_get_configname());
+ strcat(dirname, basename);
strcat(dirname, ".old");
rename(newname, dirname);
if (rename(tmpname, newname))
"# configuration written to %s\n"
"#\n"), newname);
- sym_change_count = 0;
+ sym_set_change_count(0);
return 0;
}
return 0;
}
+
+static int sym_change_count;
+static void (*conf_changed_callback)(void);
+
+void sym_set_change_count(int count)
+{
+ int _sym_change_count = sym_change_count;
+ sym_change_count = count;
+ if (conf_changed_callback &&
+ (bool)_sym_change_count != (bool)count)
+ conf_changed_callback();
+}
+
+void sym_add_change_count(int count)
+{
+ sym_set_change_count(count + sym_change_count);
+}
+
+bool conf_get_changed(void)
+{
+ return sym_change_count;
+}
+
+void conf_set_changed_callback(void (*fn)(void))
+{
+ conf_changed_callback = fn;
+}