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