| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917 | /* * 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 <regex.h>#define LKC_DIRECT_LINK#include "lkc.h"struct symbol symbol_yes = {	.name = "y",	.curr = { "y", yes },	.flags = SYMBOL_CONST|SYMBOL_VALID,}, symbol_mod = {	.name = "m",	.curr = { "m", mod },	.flags = SYMBOL_CONST|SYMBOL_VALID,}, symbol_no = {	.name = "n",	.curr = { "n", no },	.flags = SYMBOL_CONST|SYMBOL_VALID,}, symbol_empty = {	.name = "",	.curr = { "", no },	.flags = SYMBOL_VALID,};struct symbol *sym_defconfig_list;struct symbol *modules_sym;tristate modules_val;struct expr *sym_env_list;void sym_add_default(struct symbol *sym, const char *def){	struct property *prop = prop_alloc(P_DEFAULT, sym);	prop->expr = expr_alloc_symbol(sym_lookup(def, 1));}void sym_init(void){	struct symbol *sym;	char *p;	static bool inited = false;	if (inited)		return;	inited = true;	p = getenv("VERSION");	if (p) {		sym = sym_lookup("VERSION", 0);		sym->type = S_STRING;		sym->flags |= SYMBOL_AUTO;		sym_add_default(sym, p);	}}enum symbol_type sym_get_type(struct symbol *sym){	enum symbol_type type = sym->type;	if (type == S_TRISTATE) {		if (sym_is_choice_value(sym) && sym->visible == yes)			type = S_BOOLEAN;		else if (modules_val == no)			type = S_BOOLEAN;	}	return type;}const char *sym_type_name(enum symbol_type 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";	case S_OTHER:		break;	}	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_env_prop(struct symbol *sym){	struct property *prop;	for_all_properties(sym, prop, P_ENV)		return prop;	return NULL;}struct property *sym_get_default_prop(struct symbol *sym){	struct property *prop;	for_all_defaults(sym, prop) {		prop->visible.tri = expr_calc_value(prop->visible.expr);		if (prop->visible.tri != no)			return prop;	}	return NULL;}struct property *sym_get_range_prop(struct symbol *sym){	struct property *prop;	for_all_properties(sym, prop, P_RANGE) {		prop->visible.tri = expr_calc_value(prop->visible.expr);		if (prop->visible.tri != no)			return prop;	}	return NULL;}static int sym_get_range_val(struct symbol *sym, int base){	sym_calc_value(sym);	switch (sym->type) {	case S_INT:		base = 10;		break;	case S_HEX:		base = 16;		break;	default:		break;	}	return strtol(sym->curr.val, NULL, base);}static void sym_validate_range(struct symbol *sym){	struct property *prop;	int base, val, val2;	char str[64];	switch (sym->type) {	case S_INT:		base = 10;		break;	case S_HEX:		base = 16;		break;	default:		return;	}	prop = sym_get_range_prop(sym);	if (!prop)		return;	val = strtol(sym->curr.val, NULL, base);	val2 = sym_get_range_val(prop->expr->left.sym, base);	if (val >= val2) {		val2 = sym_get_range_val(prop->expr->right.sym, base);		if (val <= val2)			return;	}	if (sym->type == S_INT)		sprintf(str, "%d", val2);	else		sprintf(str, "0x%x", val2);	sym->curr.val = strdup(str);}static void sym_calc_visibility(struct symbol *sym){	struct property *prop;	tristate tri;	/* any prompt visible? */	tri = no;	for_all_prompts(sym, prop) {		prop->visible.tri = expr_calc_value(prop->visible.expr);		tri = EXPR_OR(tri, prop->visible.tri);	}	if (tri == mod && (sym->type != S_TRISTATE || modules_val == no))		tri = yes;	if (sym->visible != tri) {		sym->visible = tri;		sym_set_changed(sym);	}	if (sym_is_choice_value(sym))		return;	tri = no;	if (sym->rev_dep.expr)		tri = expr_calc_value(sym->rev_dep.expr);	if (tri == mod && sym_get_type(sym) == S_BOOLEAN)		tri = yes;	if (sym->rev_dep.tri != tri) {		sym->rev_dep.tri = tri;		sym_set_changed(sym);	}}static struct symbol *sym_calc_choice(struct symbol *sym){	struct symbol *def_sym;	struct property *prop;	struct expr *e;	/* is the user choice visible? */	def_sym = sym->def[S_DEF_USER].val;	if (def_sym) {		sym_calc_visibility(def_sym);		if (def_sym->visible != no)			return def_sym;	}	/* any of the defaults visible? */	for_all_defaults(sym, prop) {		prop->visible.tri = expr_calc_value(prop->visible.expr);		if (prop->visible.tri == no)			continue;		def_sym = prop_get_symbol(prop);		sym_calc_visibility(def_sym);		if (def_sym->visible != no)			return def_sym;	}	/* just get the first visible value */	prop = sym_get_choice_prop(sym);	expr_list_for_each_sym(prop->expr, e, def_sym) {		sym_calc_visibility(def_sym);		if (def_sym->visible != no)			return def_sym;	}	/* no choice? reset tristate value */	sym->curr.tri = no;	return NULL;}void sym_calc_value(struct symbol *sym){	struct symbol_value newval, oldval;	struct property *prop;	struct expr *e;	if (!sym)		return;	if (sym->flags & SYMBOL_VALID)		return;	sym->flags |= SYMBOL_VALID;	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:		sym->curr.val = sym->name;		sym->curr.tri = no;		return;	}	if (!sym_is_choice_value(sym))		sym->flags &= ~SYMBOL_WRITE;	sym_calc_visibility(sym);	/* set default if recursively called */	sym->curr = newval;	switch (sym_get_type(sym)) {	case S_BOOLEAN:	case S_TRISTATE:		if (sym_is_choice_value(sym) && sym->visible == yes) {			prop = sym_get_choice_prop(sym);			newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;		} else {			if (sym->visible != no) {				/* if the symbol is visible use the user value				 * if available, otherwise try the default value				 */				sym->flags |= SYMBOL_WRITE;				if (sym_has_value(sym)) {					newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri,							      sym->visible);					goto calc_newval;				}			}			if (sym->rev_dep.tri != no)				sym->flags |= SYMBOL_WRITE;			if (!sym_is_choice(sym)) {				prop = sym_get_default_prop(sym);				if (prop) {					sym->flags |= SYMBOL_WRITE;					newval.tri = EXPR_AND(expr_calc_value(prop->expr),							      prop->visible.tri);				}			}		calc_newval:			newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri);		}		if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN)			newval.tri = yes;		break;	case S_STRING:	case S_HEX:	case S_INT:		if (sym->visible != no) {			sym->flags |= SYMBOL_WRITE;			if (sym_has_value(sym)) {				newval.val = sym->def[S_DEF_USER].val;				break;			}		}		prop = sym_get_default_prop(sym);		if (prop) {			struct symbol *ds = prop_get_symbol(prop);			if (ds) {				sym->flags |= SYMBOL_WRITE;				sym_calc_value(ds);				newval.val = ds->curr.val;			}		}		break;	default:		;	}	if (sym->flags & SYMBOL_AUTO)		sym->flags &= ~SYMBOL_WRITE;	sym->curr = newval;	if (sym_is_choice(sym) && newval.tri == yes)		sym->curr.val = sym_calc_choice(sym);	sym_validate_range(sym);	if (memcmp(&oldval, &sym->curr, sizeof(oldval))) {		sym_set_changed(sym);		if (modules_sym == sym) {			sym_set_all_changed();			modules_val = modules_sym->curr.tri;		}	}	if (sym_is_choice(sym)) {		struct symbol *choice_sym;		int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE);		prop = sym_get_choice_prop(sym);		expr_list_for_each_sym(prop->expr, e, choice_sym) {			choice_sym->flags |= flags;			if (flags & SYMBOL_CHANGED)				sym_set_changed(choice_sym);		}	}}void sym_clear_all_valid(void){	struct symbol *sym;	int i;	for_all_symbols(i, sym)		sym->flags &= ~SYMBOL_VALID;	sym_add_change_count(1);	if (modules_sym)		sym_calc_value(modules_sym);}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;	if (type == S_BOOLEAN && val == mod)		return false;	if (sym->visible <= sym->rev_dep.tri)		return false;	if (sym_is_choice_value(sym) && sym->visible == yes)		return val == yes;	return val >= sym->rev_dep.tri && val <= sym->visible;}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_DEF_USER)) {		sym->flags |= SYMBOL_DEF_USER;		sym_set_changed(sym);	}	/*	 * setting a choice value also resets the new flag of the choice	 * symbol and all other choice values.	 */	if (sym_is_choice_value(sym) && val == yes) {		struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));		struct property *prop;		struct expr *e;		cs->def[S_DEF_USER].val = sym;		cs->flags |= SYMBOL_DEF_USER;		prop = sym_get_choice_prop(cs);		for (e = prop->expr; e; e = e->left.expr) {			if (e->right.sym->visible != no)				e->right.sym->flags |= SYMBOL_DEF_USER;		}	}	sym->def[S_DEF_USER].tri = val;	if (oldval != val)		sym_clear_all_valid();	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){	signed 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':		case 'm': case 'M':		case 'n': case 'N':			return true;		}		return false;	default:		return false;	}}bool sym_string_within_range(struct symbol *sym, const char *str){	struct property *prop;	int val;	switch (sym->type) {	case S_STRING:		return sym_string_valid(sym, str);	case S_INT:		if (!sym_string_valid(sym, str))			return false;		prop = sym_get_range_prop(sym);		if (!prop)			return true;		val = strtol(str, NULL, 10);		return val >= sym_get_range_val(prop->expr->left.sym, 10) &&		       val <= sym_get_range_val(prop->expr->right.sym, 10);	case S_HEX:		if (!sym_string_valid(sym, str))			return false;		prop = sym_get_range_prop(sym);		if (!prop)			return true;		val = strtol(str, NULL, 16);		return val >= sym_get_range_val(prop->expr->left.sym, 16) &&		       val <= sym_get_range_val(prop->expr->right.sym, 16);	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_within_range(sym, newval))		return false;	if (!(sym->flags & SYMBOL_DEF_USER)) {		sym->flags |= SYMBOL_DEF_USER;		sym_set_changed(sym);	}	oldval = sym->def[S_DEF_USER].val;	size = strlen(newval) + 1;	if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {		size += 2;		sym->def[S_DEF_USER].val = val = malloc(size);		*val++ = '0';		*val++ = 'x';	} else if (!oldval || strcmp(oldval, newval))		sym->def[S_DEF_USER].val = 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 *)sym->curr.val;}bool sym_is_changable(struct symbol *sym){	return sym->visible > sym->rev_dep.tri;}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;	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;}struct symbol **sym_re_search(const char *pattern){	struct symbol *sym, **sym_arr = NULL;	int i, cnt, size;	regex_t re;	cnt = size = 0;	/* Skip if empty */	if (strlen(pattern) == 0)		return NULL;	if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE))		return NULL;	for_all_symbols(i, sym) {		if (sym->flags & SYMBOL_CONST || !sym->name)			continue;		if (regexec(&re, sym->name, 0, NULL, 0))			continue;		if (cnt + 1 >= size) {			void *tmp = sym_arr;			size += 16;			sym_arr = realloc(sym_arr, size * sizeof(struct symbol *));			if (!sym_arr) {				free(tmp);				return NULL;			}		}		sym_arr[cnt++] = sym;	}	if (sym_arr)		sym_arr[cnt] = NULL;	regfree(&re);	return sym_arr;}struct symbol *sym_check_deps(struct symbol *sym);static struct symbol *sym_check_expr_deps(struct expr *e){	struct symbol *sym;	if (!e)		return NULL;	switch (e->type) {	case E_OR:	case E_AND:		sym = sym_check_expr_deps(e->left.expr);		if (sym)			return sym;		return sym_check_expr_deps(e->right.expr);	case E_NOT:		return sym_check_expr_deps(e->left.expr);	case E_EQUAL:	case E_UNEQUAL:		sym = sym_check_deps(e->left.sym);		if (sym)			return sym;		return sym_check_deps(e->right.sym);	case E_SYMBOL:		return sym_check_deps(e->left.sym);	default:		break;	}	printf("Oops! How to check %d?\n", e->type);	return NULL;}/* return NULL when dependencies are OK */struct symbol *sym_check_deps(struct symbol *sym){	struct symbol *sym2;	struct property *prop;	if (sym->flags & SYMBOL_CHECK) {		fprintf(stderr, "%s:%d:error: found recursive dependency: %s",		        sym->prop->file->name, sym->prop->lineno, sym->name);		return sym;	}	if (sym->flags & SYMBOL_CHECKED)		return NULL;	sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);	sym2 = sym_check_expr_deps(sym->rev_dep.expr);	if (sym2)		goto out;	for (prop = sym->prop; prop; prop = prop->next) {		if (prop->type == P_CHOICE || prop->type == P_SELECT)			continue;		sym2 = sym_check_expr_deps(prop->visible.expr);		if (sym2)			goto out;		if (prop->type != P_DEFAULT || sym_is_choice(sym))			continue;		sym2 = sym_check_expr_deps(prop->expr);		if (sym2)			goto out;	}out:	if (sym2)		fprintf(stderr, " -> %s%s", sym->name, sym2 == sym? "\n": "");	sym->flags &= ~SYMBOL_CHECK;	return sym2;}struct property *prop_alloc(enum prop_type type, struct symbol *sym){	struct property *prop;	struct property **propp;	prop = malloc(sizeof(*prop));	memset(prop, 0, sizeof(*prop));	prop->type = type;	prop->sym = sym;	prop->file = current_file;	prop->lineno = zconf_lineno();	/* append property to the prop list of symbol */	if (sym) {		for (propp = &sym->prop; *propp; propp = &(*propp)->next)			;		*propp = prop;	}	return prop;}struct symbol *prop_get_symbol(struct property *prop){	if (prop->expr && (prop->expr->type == E_SYMBOL ||			   prop->expr->type == E_LIST))		return prop->expr->left.sym;	return NULL;}const char *prop_get_type_name(enum prop_type type){	switch (type) {	case P_PROMPT:		return "prompt";	case P_ENV:		return "env";	case P_COMMENT:		return "comment";	case P_MENU:		return "menu";	case P_DEFAULT:		return "default";	case P_CHOICE:		return "choice";	case P_SELECT:		return "select";	case P_RANGE:		return "range";	case P_UNKNOWN:		break;	}	return "unknown";}void prop_add_env(const char *env){	struct symbol *sym, *sym2;	struct property *prop;	char *p;	sym = current_entry->sym;	sym->flags |= SYMBOL_AUTO;	for_all_properties(sym, prop, P_ENV) {		sym2 = prop_get_symbol(prop);		if (strcmp(sym2->name, env))			menu_warn(current_entry, "redefining environment symbol from %s",				  sym2->name);		return;	}	prop = prop_alloc(P_ENV, sym);	prop->expr = expr_alloc_symbol(sym_lookup(env, 1));	sym_env_list = expr_alloc_one(E_LIST, sym_env_list);	sym_env_list->right.sym = sym;	p = getenv(env);	if (p)		sym_add_default(sym, p);	else		menu_warn(current_entry, "environment variable %s undefined", env);}
 |