%{
/*
 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
 * Released under the terms of the GNU GPL v2.0.
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define PRINTD		0x0001
#define DEBUG_PARSE	0x0002
#define DEBUG_OPT	0x0004
#define DEBUG_OPT1	0x0008
#define DEBUG_OPT2	0x0010
#define DEBUG_OPT3	0x0020
#define DEBUG_OPT4	0x0040
#define DEBUG_OPT5	0x0080
#define DEBUG_DEP	0x0100

int cdebug = PRINTD;//DEBUG_OPT|-1;

#define YYERROR_VERBOSE
%}

%union
{
	char *string;
	struct expr *expr;
	struct symbol *symbol;
	struct statement *stmt;
}

%token <string> T_WORD
%token <string> T_WORD_QUOTE
%token <string> T_WORD_DQUOTE
%token T_MAINMENU_NAME
%token T_MAINMENU_OPTION
%token T_NEXT_COMMENT
%token T_ENDMENU
%token T_COMMENT
%token T_BOOL
%token T_HEX
%token T_INT
%token T_STRING
%token T_TRISTATE
%token T_DEFINE_BOOL
%token T_DEFINE_HEX
%token T_DEFINE_INT
%token T_DEFINE_STRING
%token T_DEFINE_TRISTATE
%token T_DEP_BOOL
%token T_DEP_MBOOL
%token T_DEP_TRISTATE
%token T_UNSET
%token T_CHOICE
%token T_SOURCE
%token T_IF
%token T_ELSE
%token T_FI
%token T_THEN
%token T_UNEQUAL
%token <string> T_SH_COMMENT

%type <string> prompt
%type <string> word
%type <string> menu_comment
%type <symbol> symbol
%type <symbol> symbol_ref
%type <symbol> symbol_other
%type <stmt> statement
%type <expr> expr
%type <expr> dep
%type <expr> deps
%type <stmt> comment_opt

%left T_OR
%left T_AND
%nonassoc '!'
%left '=' T_UNEQUAL

%{
#include "cml1.h"

extern int cml1lex(void);
static void cml1error(const char *err);
extern int lineno(void);
extern void scan_nextfile(char *file);

struct statement *create_stmt(int token);
struct statement *add_stmt(struct symbol *sym, int token,
			   char *prompt, struct symbol *def);
void print_expr(int mask, struct expr *e, int prevtoken);
void print_dep(int mask, struct statement *stmt);

void eliminate_eq(int token, struct expr **ep1, struct expr **ep2);
static inline int expr_is_yes(struct expr *e);
static inline int expr_is_no(struct expr *e);
struct expr *trans_dep_bool(struct expr *e);
struct expr *trans_bool_expr(struct expr *e);
struct expr *fixup_expr(struct expr *e);
struct expr *trans_expr(struct expr *e);
void extract_eq(int token, struct expr **ep, struct expr **ep1, struct expr **ep2);
struct expr *extract_eq_and(struct expr **ep1, struct expr **ep2);
struct expr *extract_eq_or(struct expr **ep1, struct expr **ep2);
struct expr *optimize_expr_or(struct expr *e);
struct expr *eliminate_dups(struct expr *e);
struct expr *eliminate_yn(struct expr *e);
void eliminate_dep(struct symbol *sym, struct expr *dep);
int contains_expr(struct expr *dep, struct expr *e);

#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
#define fprintd(f, mask, fmt...) if (cdebug & (mask)) fprintf(f, fmt)

static struct expr *if_expr;
static struct menu root_menu;
static struct menu *current_menu = &root_menu;

struct symbol *symbol_hash[257];
%}
%%
input:	line
;

line:	/* empty */
;

line:	line statement
;

line:	line '\n'
;

statement: T_SH_COMMENT
{
	$$ = create_stmt(T_SH_COMMENT);
	$$->text = $1;
};

statement: T_MAINMENU_NAME prompt '\n'
{
	$$ = create_stmt(T_MAINMENU_NAME);
	$$->text = $2;
	printd(DEBUG_PARSE, "%d:mainmenu_name: %s\n", lineno(), $2);
};

statement: T_COMMENT prompt '\n'
{
	$$ = create_stmt(T_COMMENT);
	$$->text = $2;
	$$->dep = trans_expr(fixup_expr(expr_copy(if_expr)));
	printd(DEBUG_PARSE, "%d:comment: %s\n", lineno(), $2);
};

statement: T_BOOL prompt symbol dep '\n'
{
	$$ = add_stmt($3, T_BOOL, $2, NULL);
	printd(DEBUG_PARSE, "%d:bool: %s %s\n", lineno(), $2, $3->name);
	if ($4) {
		printf("%s:%d: warning ignoring dependencies\n", current_file->name, current_file->lineno);
		expr_free($4);
	}
	print_dep(DEBUG_PARSE, $$);
};

statement: T_HEX prompt symbol symbol_other '\n'
{
	$$ = add_stmt($3, T_HEX, $2, NULL);
	add_stmt($3, T_DEFINE_HEX, NULL, $4);
	printd(DEBUG_PARSE, "%d:hex: %s %s %s\n", lineno(), $2, $3->name, $4->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_INT prompt symbol symbol_other '\n'
{
	$$ = add_stmt($3, T_INT, $2, NULL);
	add_stmt($3, T_DEFINE_INT, NULL, $4);
	printd(DEBUG_PARSE, "%d:int: %s %s %s\n", lineno(), $2, $3->name, $4->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_STRING prompt symbol symbol_other '\n'
{
	$$ = add_stmt($3, T_STRING, $2, NULL);
	add_stmt($3, T_DEFINE_STRING, NULL, $4);
	printd(DEBUG_PARSE, "%d:string: %s %s %s\n", lineno(), $2, $3->name, $4->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_TRISTATE prompt symbol dep '\n'
{
	$$ = add_stmt($3, T_TRISTATE, $2, NULL);
	printd(DEBUG_PARSE, "%d:tristate: %s %s\n", lineno(), $2, $3->name);
	if ($4) {
		printf("%s:%d: warning ignoring dependencies\n", current_file->name, current_file->lineno);
		expr_free($4);
	}
	print_dep(DEBUG_PARSE, $$);
};

statement: T_DEFINE_BOOL symbol symbol_ref comment_opt '\n'
{
	$$ = add_stmt($2, T_DEFINE_BOOL, NULL, $3);
	printd(DEBUG_PARSE, "%d:define_bool: %s %s\n", lineno(), $2->name, $3->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_DEFINE_HEX symbol symbol_other '\n'
{
	$$ = add_stmt($2, T_DEFINE_HEX, NULL, $3);
	printd(DEBUG_PARSE, "%d:define_hex: %s %s\n", lineno(), $2->name, $3->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_DEFINE_INT symbol symbol_other comment_opt '\n'
{
	$$ = add_stmt($2, T_DEFINE_INT, NULL, $3);
	printd(DEBUG_PARSE, "%d:define_int: %s %s\n", lineno(), $2->name, $3->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_DEFINE_STRING symbol symbol_other '\n'
{
	$$ = add_stmt($2, T_DEFINE_STRING, NULL, $3);
	printd(DEBUG_PARSE, "%d:define_string: %s %s\n", lineno(), $2->name, $3->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_DEFINE_TRISTATE symbol symbol_ref '\n'
{
	$$ = add_stmt($2, T_DEFINE_TRISTATE, NULL, $3);
	printd(DEBUG_PARSE, "%d:define_tristate: %s %s\n", lineno(), $2->name, $3->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_DEP_BOOL prompt symbol dep '\n'
{
	$$ = add_stmt($3, T_DEP_BOOL, $2, NULL);
	if (!($$->dep2 = $4))
		printf("%s:%d: warning dep_bool without dependencies\n", current_file->name, current_file->lineno);
	printd(DEBUG_PARSE, "%d:dep_bool: %s %s\n", lineno(), $2, $3->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_DEP_MBOOL prompt symbol dep '\n'
{
	$$ = add_stmt($3, T_DEP_MBOOL, $2, NULL);
	if (!($$->dep2 = $4))
		printf("%s:%d: warning dep_mbool without dependencies\n", current_file->name, current_file->lineno);
	printd(DEBUG_PARSE, "%d:dep_mbool: %s %s\n", lineno(), $2, $3->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_DEP_TRISTATE prompt symbol dep '\n'
{
	$$ = add_stmt($3, T_DEP_TRISTATE, $2, NULL);
	if (!($$->dep2 = $4))
		printf("%s:%d: warning dep_tristate without dependencies\n", current_file->name, current_file->lineno);
	printd(DEBUG_PARSE, "%d:dep_tristate: %s %s\n", lineno(), $2, $3->name);
	print_dep(DEBUG_PARSE, $$);
};

statement: T_UNSET deps '\n'
{
	//add_stmt($2, T_UNSET, NULL, NULL);
	printd(DEBUG_PARSE, "%d:unset\n", lineno());
	print_dep(DEBUG_PARSE, NULL);
};

statement: T_CHOICE prompt word word '\n'
{
	struct statement *stmt;
	struct symbol *sym;
	struct expr **depp;
	char *ptr, *name, *def, end;

	printd(DEBUG_PARSE, "%d:choice: %s %s %s\n", lineno(), $2, $3, $4);
	$$ = create_stmt('^');
	$$->text = $2;
	ptr = $3;
	depp = &$$->dep;
	do {
		while (isspace(*ptr))
			ptr++;
		if (!*ptr)
			break;
		def = ptr;
		while (*ptr && !isspace(*ptr))
			ptr++;
		*ptr++ = 0;
		//def = lookup_symbol(name, SYMBOL_OTHER);
		while (isspace(*ptr))
			ptr++;
		if (!*ptr)
			break;
		name = ptr;
		while (*ptr && !isspace(*ptr))
			ptr++;
		end = *ptr;
		*ptr++ = 0;
		sym = lookup_symbol(name, SYMBOL_BOOLEAN);
		if (!$$->def) {
			if (!strncmp(def, $4, strlen($4)))
				$$->def = sym;
		}
		sym->flags |= SYMBOL_CHOICE;
		stmt = add_stmt(sym, T_BOOL, strdup(def), NULL);
		if (!stmt->next) {
			stmt->next = $$;
		} else
			printf("warning: unexpected stmt for choice symbol %s\n", sym->name);
		*depp = expr_alloc_one('^', NULL);
		(*depp)->right.sym = sym;
		depp = &(*depp)->left.expr;
		printd(DEBUG_PARSE, "added symbol: %s %s\n", sym->name, stmt->text);
	} while (end);
	print_dep(DEBUG_PARSE, NULL);
};

statement: T_IF '[' expr ']' semi_or_nl T_THEN comment_opt '\n'
{
	struct expr *e;
	printd(DEBUG_PARSE, "%d:if ", lineno());
	print_expr(DEBUG_PARSE, $3, 0);
	printd(DEBUG_PARSE, "\n");
	e = expr_alloc_one(T_IF, $3);
	e->right.expr = if_expr;
	if_expr = e;
	$$ = NULL;
};

semi_or_nl: ';' | '\n'
;

statement: T_ELSE comment_opt '\n'
{
	printd(DEBUG_PARSE, "%d:else\n", lineno());
	if_expr->token = T_ELSE;
	$$ = NULL;
};

statement: T_FI comment_opt '\n'
{
	struct expr *e;
	printd(DEBUG_PARSE, "%d:fi\n", lineno());
	e = if_expr;
	if (e) {
		if_expr = e->right.expr;
		expr_free(e->left.expr);
		free(e);
	} else
		YYABORT;
	$$ = NULL;
};

statement: T_MAINMENU_OPTION T_NEXT_COMMENT menu_comment '\n'
{
	printd(DEBUG_PARSE, "%d:mainmenu_option: %s\n", lineno(), $3);
	$$ = create_stmt(T_MAINMENU_OPTION);
	$$->text = $3;
	$$->dep = trans_expr(fixup_expr(expr_copy(if_expr)));
	current_menu = $$->menu;
};

menu_comment: '\n' T_COMMENT prompt { $$ = $3; }
	    | '\n' menu_comment { $$ = $2; }
;

statement: T_ENDMENU '\n'
{
	$$ = create_stmt(T_ENDMENU);
	printd(DEBUG_PARSE, "%d:endmenu\n", lineno());
	current_menu = current_menu->parent;
};

statement: T_SOURCE word '\n'
{
	static char helppath[256];
	char *p;
	$$ = create_stmt(T_SOURCE);
	$$->text = $2;
	printd(DEBUG_PARSE, "%d:source: %s\n", lineno(), $2);
	strcpy(helppath, $2);
	p = strrchr(helppath, '.');
	if (p) {
		strcpy(p, ".help");
		helplex(helppath);
	}
	scan_nextfile($2);
};

comment_opt:	/* empty */	{ $$ = NULL; }
	|	T_SH_COMMENT
{
	$$ = create_stmt(T_SH_COMMENT);
	$$->text = $1;
};

prompt:   T_WORD_QUOTE
	| T_WORD_DQUOTE
;

word:	  T_WORD
	| T_WORD_QUOTE
	| T_WORD_DQUOTE
;

dep:	  /* empty */		{ $$ = NULL; }
	| deps			{ $$ = $1; }
;

deps:	  symbol_ref		{ $$ = expr_alloc_symbol($1); }
	| deps symbol_ref	{ $$ = expr_alloc_two('&', $1, expr_alloc_symbol($2)); }
;

symbol:	T_WORD			{ $$ = lookup_symbol($1, SYMBOL_UNKNOWN); free($1); }
;

expr:	  symbol_ref '=' symbol_ref		{ $$ = expr_alloc_comp('=', $1, $3); }
	| symbol_ref T_UNEQUAL symbol_ref	{ $$ = expr_alloc_comp(T_UNEQUAL, $1, $3); }
	| '!' expr		{ $$ = expr_alloc_one('!', $2); }
	| expr T_AND expr	{ $$ = expr_alloc_two(T_AND, $1, $3); }
	| expr T_OR expr	{ $$ = expr_alloc_two(T_OR, $1, $3); }
;

symbol_ref: T_WORD		{ $$ = lookup_symbol_ref($1); free($1); }
	  | T_WORD_QUOTE	{ $$ = lookup_symbol_ref($1); free($1); }
	  | T_WORD_DQUOTE	{ $$ = lookup_symbol_ref($1); free($1); }
;

symbol_other: T_WORD		{ $$ = lookup_symbol($1, SYMBOL_OTHER); free($1); }
	    | T_WORD_QUOTE	{ $$ = lookup_symbol($1, SYMBOL_OTHER); free($1); }
	    | T_WORD_DQUOTE	{ $$ = lookup_symbol($1, SYMBOL_OTHER); free($1); }
;

%%

static void yyerror(const char *err)
{
	printf("%s:%d: %s\n", current_file->name, current_file->lineno + 1, err);
	exit(1);
}

void print_dep(int mask, struct statement *stmt)
{
	printd(mask, "dep: ");
	if (stmt) {
		if (stmt->dep2)
			printd(mask, "[");
		print_expr(mask, stmt->dep, 0);
		if (stmt->dep2) {
			printd(mask, " & ");
			print_expr(mask, stmt->dep2, 0);
			printd(mask, "]");
		}
	} else
		print_expr(mask, if_expr, 0);
	printd(mask, "\n");
}

struct symbol *lookup_symbol(char *name, int type)
{
	struct symbol *symbol;
	char *ptr;
	int hash = 0;

	//printf("lookup: %s -> ", name);
	if (type != SYMBOL_OTHER && !strncmp(name, "CONFIG_", 7)) {
		name += 7;
	} else {
		//printf("Warning: Invalid symbol name '%s'\n", name);
		type = SYMBOL_OTHER;
	}

	for (ptr = name; *ptr; ptr++)
		hash += *ptr;

	for (symbol = symbol_hash[hash&255]; symbol; symbol = symbol->next) {
		if (!strcmp(symbol->name, name)) {
			//printf("h:%p\n", symbol);
			if (type == SYMBOL_BOOLEAN)
				symbol->type = SYMBOL_BOOLEAN;
			return symbol;
		}
	}

	symbol = malloc(sizeof(*symbol));
	memset(symbol, 0, sizeof(*symbol));
	symbol->name = strdup(name);
	symbol->type = type;

	symbol->next = symbol_hash[hash&255];
	symbol_hash[hash&255] = symbol;

	//printf("n:%p\n", symbol);
	return symbol;
}

struct symbol *lookup_symbol_ref(char *name)
{
	if (name[0] == 'y' && !name[1])
		return &symbol_yes;
	if (name[0] == 'm' && !name[1])
		return &symbol_mod;
	if (name[0] == 'n' && !name[1])
		return &symbol_no;
	if (name[0] == '$')
		return lookup_symbol(name+1, SYMBOL_UNKNOWN);
	return lookup_symbol(name, SYMBOL_OTHER);
}

struct statement *create_stmt(int token)
{
	struct statement *stmt;
	struct menu *menu, **menup;

	stmt = malloc(sizeof(*stmt));
	memset(stmt, 0, sizeof(*stmt));
	stmt->token = token;
	stmt->file = current_file;
	stmt->lineno = lineno();

	if (!current_file->stmt)
		current_file->stmt = stmt;
	if (current_file->last_stmt)
		current_file->last_stmt->next_pos = stmt;
	current_file->last_stmt = stmt;

	menu = malloc(sizeof(*menu));
	memset(menu, 0, sizeof(*menu));
	menu->prompt = stmt;
	menu->parent = current_menu;
	for (menup = &current_menu->list; *menup; menup = &(*menup)->next)
		;
	*menup = menu;

	stmt->menu = menu;

	return stmt;
}

struct statement *add_stmt(struct symbol *sym, int token, char *prompt, struct symbol *def)
{
	struct statement *stmt = create_stmt(token);

	stmt->dep = expr_copy(if_expr);
	stmt->text = prompt;
	stmt->def = def;

	stmt->sym = sym;
	stmt->next = sym->stmt;
	sym->stmt = stmt;

	return stmt;
}

void print_quoted_string(FILE *out, const char *str)
{
	const char *p;
	int len;

	putc('"', out);
	while ((p = strchr(str, '"'))) {
		len = p - str;
		if (len)
			fprintf(out, "%.*s", len, str);
		fputs("\\\"", out);
		str += len + 1;
	}
	fputs(str, out);
	putc('"', out);
}

const char *skip_space(const char *p)
{
	while (*p == ' ')
		p++;
	return p;
}

void do_print_symbol(FILE *out, struct symbol *sym)
{
	struct statement *zero_stmt, *stmt, *stmt2;
	struct expr *dep, *dep2;

#define VALID_STMT(s) (s->token && s->token != '^' && s->menu && s->menu->parent == current_menu)

	dep = NULL;
	for (stmt = sym->stmt; stmt; stmt = stmt->next) {
		if (!VALID_STMT(stmt))
			continue;
		if (!dep) {
			if (!stmt->dep)
				break;
			dep = stmt->dep;
			stmt->dep = NULL;
		} else {
			struct expr *e = dep;
			if (stmt->dep) {
				dep = extract_eq_and(&e, &stmt->dep);
				if (expr_is_yes(e)) {
					expr_free(e);
					continue;
				}
			} else
				dep = NULL;
			for (stmt2 = sym->stmt; stmt != stmt2; stmt2 = stmt2->next) {
				if (!VALID_STMT(stmt2))
					continue;
				stmt2->dep = !stmt2->dep ? expr_copy(e) :
					expr_alloc_two(T_AND, expr_copy(e), stmt2->dep);
			}
			expr_free(e);
			if (!dep)
				break;
		}
	}

	zero_stmt = NULL;
	for (stmt = sym->stmt; stmt; stmt = stmt->next) {
		if (!stmt->token)
			zero_stmt = stmt;
		if (VALID_STMT(stmt))
			break;
	}
	if (!stmt) {
		if (sym->flags & SYMBOL_PRINTED)
			return;
		if (!zero_stmt)
			return;
		for (stmt = sym->stmt; stmt; stmt = stmt->next) {
			if (stmt->token)
				return;
		}
		stmt = zero_stmt;
	}

	fprintf(out, "config %s\n", sym->name);

	switch (sym->type) {
	case SYMBOL_BOOLEAN: fputs("\tbool", out); break;
	case SYMBOL_TRISTATE: fputs("\ttristate", out); break;
	case SYMBOL_INT: fputs("\tint", out); break;
	case SYMBOL_HEX: fputs("\thex", out); break;
	case SYMBOL_STRING: fputs("\tstring", out); break;
	default: fputs("\t#unknown type?", out); break;
	}

	if (stmt->text) {
		fputc(' ', out);
		print_quoted_string(out, skip_space(stmt->text));
		if (!expr_is_yes(stmt->dep)) {
			fputs(" if ", out);
			fprint_expr(out, stmt->dep, 0);
		}
		stmt->menu = NULL;
		stmt = stmt->next;
	}
	fputc('\n', out);

	if (sym->dep || dep) {
		dep = expr_alloc_and(expr_copy(sym->dep), dep);
		dep = eliminate_yn(dep);
		if (current_menu->prompt && current_menu->prompt->dep) {
			dep2 = expr_copy(current_menu->prompt->dep);
			switch (sym->type) {
			case SYMBOL_BOOLEAN: case SYMBOL_INT: case SYMBOL_HEX: case SYMBOL_STRING:
				dep2 = trans_bool_expr(dep2);
				break;
			}
			eliminate_eq(T_AND, &dep, &dep2);
			dep = eliminate_yn(dep);
#if 0
			dep2 = eliminate_yn(dep2);
			if (!expr_is_yes(dep2)) {
				printf("Warning, I'm still confused (%s)?\n", sym->name);
				print_expr(PRINTD, dep, 0);
				printf(" - ");
				print_expr(PRINTD, dep2, 0);
				printf("\n");
			}
#endif
			expr_free(dep2);
		}
		if (!expr_is_yes(dep)) {
			fputs("\tdepends on ", out);
			fprint_expr(out, eliminate_yn(dep), 0);
			fputc('\n', out);
		}
	}
	for (; stmt; stmt = stmt->next) {
		if (!VALID_STMT(stmt))
			continue;
		if (stmt->text) {
			;
		} else if (stmt->def) {
			if (sym->flags & SYMBOL_CHOICE)
				printf("Choice value %s has default value!\n", sym->name);
			fputs( "\tdefault ", out);
			switch (sym->type) {
			case SYMBOL_BOOLEAN:
			case SYMBOL_TRISTATE:
				fputs(stmt->def->name, out);
				break;
			default:
				print_quoted_string(out, stmt->def->name);
				break;
			}
			if (!expr_is_yes(stmt->dep)) {
				fputs(" if ", out);
				fprint_expr(out, eliminate_yn(stmt->dep), 0);
			}
			fputc('\n', out);
			stmt->menu = NULL;
		}
	}
	if (sym->help && !(sym->flags & SYMBOL_PRINTED)) {
		int len = strlen(sym->help);
		char *p, *p2;
		int cnt;

		while (sym->help[--len] == '\n')
			sym->help[len] = 0;
		for (p = sym->help, cnt = 0; (p = strchr(p, '\n')); p++, cnt++)
			;
		if (cnt >= 9)
			fprintf(out, "\t---help---\n");
		else
			fprintf(out, "\thelp\n");
		for (p = sym->help;; p = p2 + 1) {
			while (*p == ' ')
				p++;
			if (*p == '\n') {
				fputc('\n', out);
				p2 = p;
				continue;
			}
			p2 = strchr(p, '\n');
			if (!p2)
				break;
			fprintf(out, "\t  %.*s\n", p2 - p, p);
		}
		fprintf(out, "\t  %s\n", p);
	}
	fputc('\n', out);
	for (stmt = sym->stmt; stmt; stmt = stmt->next) {
		if (!VALID_STMT(stmt))
			continue;
		if (stmt->text) {
			fprintf(out, "config %s\n", sym->name);
			fputs( "\tprompt ", out);
			print_quoted_string(out, skip_space(stmt->text));
			fputc('\n', out);
			dep2 = expr_alloc_and(expr_copy(dep), expr_copy(stmt->dep));
			dep2 = eliminate_dups(dep2);
			if (!expr_is_yes(dep2)) {
				fputs("\tdepends on ", out);
				fprint_expr(out, dep2, 0);
				fputc('\n', out);
			}
			expr_free(dep2);
			fputc('\n', out);
			stmt->menu = NULL;
		}
	}
	sym->flags |= SYMBOL_PRINTED;
	expr_free(dep);
}

void print_choice(FILE *out, struct symbol *sym)
{
	struct statement *stmt;
	struct expr *lst, *dep, *tmp, *tmp2;

	if (sym->flags & SYMBOL_PRINTED)
		return;

	for (stmt = sym->stmt; stmt; stmt = stmt->next)
		if (stmt->token == '^')
			break;
	if (!stmt)
		//warn?
		return;

	fputs("choice\n", out);
	if (stmt->text) {
		fputs("\tprompt ", out);
		print_quoted_string(out, skip_space(stmt->text));
		fputc('\n', out);
	}

	dep = NULL;
	for (lst = stmt->dep; lst; lst = lst->left.expr) {
		sym = lst->right.sym;
		if (sym == &symbol_no) {
			fputs("\toptional\n", out);
			continue;
		}
		if (!dep) {
			if (!sym->dep)
				break;
			dep = sym->dep;
			sym->dep = NULL;
		} else {
			tmp = dep;
			if (sym->dep) {
				dep = extract_eq_and(&tmp, &sym->dep);
				if (expr_is_yes(tmp)) {
					expr_free(tmp);
					continue;
				}
			} else
				dep = NULL;
			for (tmp2 = stmt->dep; tmp2 != lst; tmp2 = tmp2->left.expr) {
				sym = tmp2->right.sym;
				if (sym == &symbol_no)
					continue;
				sym->dep = !sym->dep ? expr_copy(tmp) :
					expr_alloc_two(T_AND, expr_copy(tmp), sym->dep);
				sym->dep = eliminate_yn(sym->dep);
			}
			expr_free(tmp);
			if (!dep)
				break;
		}
	}
	for (;lst; lst = lst->left.expr) {
		sym = lst->right.sym;
		if (sym == &symbol_no) {
			fputs("\toptional\n", out);
			continue;
		}
	}
	if (!expr_is_yes(dep)) {
		fputs("\tdepends on ", out);
		fprint_expr(out, dep, 0);
		fputc('\n', out);
	}
	if (stmt->def)
		fprintf(out, "\tdefault %s\n", stmt->def->name);

	fputc('\n', out);

	for (lst = stmt->dep; lst; lst = lst->left.expr) {
		sym = lst->right.sym;
		if (sym == &symbol_no)
			continue;
		do_print_symbol(out, sym);
		sym->flags |= SYMBOL_PRINTED;
	}

	fputs("endchoice\n\n", out);
}

void print_symbols(FILE *out, struct symbol *sym)
{
	//if (sym->flags & SYMBOL_PRINTED)
	//	return;
	//sym->flags |= SYMBOL_PRINTED;

	if (sym->flags & SYMBOL_CHOICE) {
		print_choice(out, sym);
		return;
	}
	do_print_symbol(out, sym);
}

char *gen_filename(const char *name)
{
	static char newname[128];
	char *p;

	strcpy(newname, name);
	p = strrchr(newname, '/');
	if (!p)
		p = newname;
	else
		p++;
#if 0
	p = strrchr(p, '.');
	if (!p)
		p = newname + strlen(newname);
	strcpy(p, ".new");
#else
#if 0
	memcpy(p, "Build", 5);
	memmove(p + 5, p + 6, strlen(p) - 5);
	p = strrchr(p, '.');
	if (!p)
		p = newname + strlen(newname);
	strcpy(p, ".conf");
#else
	memmove(p + 1, p, strlen(p) + 1);
	p[0] = 'K';
	p[1] = 'c';
	p = strrchr(p, '.');
	*p = 0;
#endif
#endif
	return newname;
}

void print_files(FILE *all_out, const char *name)
{
	struct file *file;
	FILE *out = all_out;
	struct symbol *sym;
	struct statement *stmt;
	struct expr *dep1, *dep2;
	char *p;

	print_type = 0;

	file = lookup_file(name);
	if (!file) {
		fprintf(stderr, "# file %s not found!\n\n", file->name);
		return;
	} else if (file->flags & FILE_PRINTED) {
		fprintf(stderr, "# file %s skipped\n\n", file->name);
		return;
	}

	if (!out) {
		p = gen_filename(file->name);
		strcat(p, ".lkc");
		out = fopen(p, "w");
		if (!out) {
			printf("unable to open %s for writing!\n", p);
			exit(1);
		}
	}

	if (all_out)
		fprintf(all_out, "# file %s\n\n", file->name);
	file->flags |= FILE_PRINTED;

	for (stmt = file->stmt; stmt; stmt = stmt->next_pos) {
		sym = stmt->sym;
		if (!sym) {
			switch (stmt->token) {
			case T_MAINMENU_NAME:
				fputs("\nmainmenu ", out);
				print_quoted_string(out, skip_space(stmt->text));
				fputs("\n\n", out);
				break;
			case T_COMMENT:
				fputs("comment ", out);
				print_quoted_string(out, skip_space(stmt->text));
				stmt->dep = eliminate_dups(trans_expr(stmt->dep));
				dep1 = expr_copy(stmt->dep);
				if (dep1 && current_menu->prompt && current_menu->prompt->dep) {
					dep2 = expr_copy(current_menu->prompt->dep);
					eliminate_eq(T_AND, &dep1, &dep2);
					dep1 = eliminate_yn(dep1);
					dep2 = eliminate_yn(dep2);
					if (!expr_is_yes(dep2)) {
						printf("Warning, I'm confused!\n");
					}
					expr_free(dep2);
				}
				if (!expr_is_yes(dep1)) {
					dep1 = trans_bool_expr(dep1);
					fputs("\n\tdepends on ", out);
					fprint_expr(out, dep1, 0);
				}
				expr_free(dep1);
				fputs("\n\n", out);
				break;
			case T_SH_COMMENT:
				fprintf(out, "%s\n", stmt->text);
				break;
			case T_MAINMENU_OPTION:
				fputs("\nmenu ", out);
				print_quoted_string(out, skip_space(stmt->text));
				stmt->dep = eliminate_dups(trans_expr(stmt->dep));
				dep1 = expr_copy(stmt->dep);
				if (dep1 && current_menu->prompt && current_menu->prompt->dep) {
					dep2 = expr_copy(current_menu->prompt->dep);
					eliminate_eq(T_AND, &dep1, &dep2);
					dep1 = eliminate_yn(dep1);
					dep2 = eliminate_yn(dep2);
					if (!expr_is_yes(dep2)) {
						printf("Warning, I'm confused!\n");
					}
					expr_free(dep2);
				}
				if (!expr_is_yes(dep1)) {
					fputs("\n\tdepends on ", out);
					fprint_expr(out, dep1, 0);
				}
				expr_free(dep1);
				fputs("\n\n", out);
				current_menu = stmt->menu;
				break;
			case T_ENDMENU:
				fputs("endmenu\n\n", out);
				current_menu = current_menu->parent;
				break;
			case T_SOURCE:
				fprintf(out, "%ssource ", all_out ? "#" : "");
				print_quoted_string(out, gen_filename(stmt->text));
				fputs("\n\n", out);
				print_files(all_out, stmt->text);
				break;
			}
		} else
			print_symbols(out, sym);
	}

	if (all_out)
		fprintf(out, "# fileend %s\n\n", file->name);
}

static int trans_count;

void eliminate_eq(int token, struct expr **ep1, struct expr **ep2)
{
#define e1 (*ep1)
#define e2 (*ep2)

	if (e1->token == token) {
		eliminate_eq(token, &e1->left.expr, &e2);
		eliminate_eq(token, &e1->right.expr, &e2);
		return;
	}
	if (e2->token == token) {
		eliminate_eq(token, &e1, &e2->left.expr);
		eliminate_eq(token, &e1, &e2->right.expr);
		return;
	}
	if (e1->token == T_WORD && e2->token == T_WORD &&
	    e1->left.sym == e2->left.sym && (e1->left.sym->flags & (SYMBOL_YES|SYMBOL_NO)))
		return;
	if (!expr_eq(e1, e2))
		return;
	trans_count++;
	expr_free(e1); expr_free(e2);
	switch (token) {
	case T_OR:
		e1 = expr_alloc_symbol(&symbol_no);
		e2 = expr_alloc_symbol(&symbol_no);
		break;
	case T_AND:
		e1 = expr_alloc_symbol(&symbol_yes);
		e2 = expr_alloc_symbol(&symbol_yes);
		break;
	}
#undef e1
#undef e2
}

int expr_eq(struct expr *e1, struct expr *e2)
{
	int res, old_count;

	if (e1->token != e2->token)
		return 0;
	switch (e1->token) {
	case '=':
	case T_UNEQUAL:
		return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
	case T_WORD:
		return e1->left.sym == e2->left.sym;
	case '!':
		return expr_eq(e1->left.expr, e2->left.expr);
	case T_AND:
	case T_OR:
		e1 = expr_copy(e1);
		e2 = expr_copy(e2);
		old_count = trans_count;
		eliminate_eq(e1->token, &e1, &e2);
		e1 = eliminate_yn(e1);
		e2 = eliminate_yn(e2);
		res = (e1->token == T_WORD &&
		       e2->token == T_WORD &&
		       e1->left.sym == e2->left.sym);
		expr_free(e1);
		expr_free(e2);
		trans_count = old_count;
		return res;
	}
	print_expr(PRINTD, e1, 0);
	printf(" = ");
	print_expr(PRINTD, e2, 0);
	printf(" ?\n");
	return 0;
}

/*
 * dep_bool FOO -> FOO=y
 */
struct expr *trans_dep_bool(struct expr *e)
{
	switch (e->token) {
	case '&':
	case T_AND:
		e->left.expr = trans_dep_bool(e->left.expr);
		e->right.expr = trans_dep_bool(e->right.expr);
		break;
	case T_WORD:
		if (e->left.sym->type == SYMBOL_TRISTATE) {
			e->token = '=';
			e->right.sym = &symbol_yes;
		}
		break;
	}
	return e;
}

/*
 * bool FOO!=n => FOO
 */
struct expr *trans_bool_expr(struct expr *e)
{
	if (!e)
		return NULL;
	switch (e->token) {
	case T_AND:
	case T_OR:
	case '!':
		e->left.expr = trans_bool_expr(e->left.expr);
		e->right.expr = trans_bool_expr(e->right.expr);
		break;
	case T_UNEQUAL:
		// FOO!=n -> FOO
		if (e->left.sym->type == SYMBOL_TRISTATE) {
			if (e->right.sym == &symbol_no) {
				e->token = T_WORD;
				e->right.sym = NULL;
			}
		}
		break;
	}
	return e;
}

struct expr *fixup_expr(struct expr *e)
{
	struct expr *tmp;

	if (!e)
		return NULL;

	switch (e->token) {
	case T_IF:
		// if expr if expr ... -> expr && expr ...
		print_expr(DEBUG_OPT1, e, 0);
		printd(DEBUG_OPT1, " -> ");
		tmp = e->left.expr;
		if (!e->right.expr) {
			free(e);
			e = fixup_expr(tmp);
			print_expr(DEBUG_OPT1, e, 0);
			printd(DEBUG_OPT1, "\n");
			return e;
		}
		e->left.expr = e->right.expr;
		e->right.expr = tmp;
		e->token = T_AND;
		print_expr(DEBUG_OPT1, e, 0);
		printd(DEBUG_OPT1, "\n");
		break;
	case T_ELSE:
		// if expr else expr ... -> expr && !expr ...
		print_expr(DEBUG_OPT1, e, 0);
		printd(DEBUG_OPT1, " -> ");
		e->token = '!';
		if (!e->right.expr) {
			print_expr(DEBUG_OPT1, e, 0);
			printd(DEBUG_OPT1, "\n");
			break;
		}
		tmp = e;
		e = expr_alloc_two(T_AND, e->right.expr, e);
		tmp->right.expr = NULL;
		print_expr(DEBUG_OPT1, e, 0);
		printd(DEBUG_OPT1, "\n");
		break;
	case '&':
		// expr & expr -> expr && expr
		print_expr(DEBUG_OPT1, e, 0);
		printd(DEBUG_OPT1, " -> ");
		e->token = T_AND;
		print_expr(DEBUG_OPT1, e, 0);
		printd(DEBUG_OPT1, "\n");
		break;
	case '=':
	case T_UNEQUAL:
		if (!(e->left.sym->flags & SYMBOL_CONST))
			return e;
		if (e->right.sym->flags & SYMBOL_CONST) {
			if (e->left.sym == e->right.sym) {
				e->left.sym = e->token == '=' ? &symbol_yes : &symbol_no;
			} else {
				e->left.sym = e->token != '=' ? &symbol_yes : &symbol_no;
			}
			e->token = T_WORD;
			e->right.sym = NULL;
		} else {
			// y/n/m = sym -> sym = y/n/m
			struct symbol *sym;
			print_expr(DEBUG_OPT1, e, 0);
			printd(DEBUG_OPT1, " -> ");
			sym = e->left.sym;
			e->left.sym = e->right.sym;
			e->right.sym = sym;
			print_expr(DEBUG_OPT1, e, 0);
			printd(DEBUG_OPT1, "\n");
		}
		return e;
	case T_WORD:
	case '^':
		return e;
	}
	e->left.expr = fixup_expr(e->left.expr);
	e->right.expr = fixup_expr(e->right.expr);
	return e;
}

struct expr *join_or(struct expr *e1, struct expr *e2)
{
	struct expr *tmp;
	struct symbol *sym1, *sym2;

	if (expr_eq(e1, e2))
		return expr_copy(e1);
	if (e1->token != '=' && e1->token != T_UNEQUAL && e1->token != T_WORD && e1->token != '!')
		return NULL;
	if (e2->token != '=' && e2->token != T_UNEQUAL && e2->token != T_WORD && e2->token != '!')
		return NULL;
	if (e1->token == '!') {
		tmp = e1->left.expr;
		if (tmp->token != '=' && tmp->token != T_UNEQUAL && tmp->token != T_WORD)
			return NULL;
		sym1 = tmp->left.sym;
	} else
		sym1 = e1->left.sym;
	if (e2->token == '!') {
		if (e2->left.expr->token != T_WORD)
			return NULL;
		sym2 = e2->left.expr->left.sym;
	} else
		sym2 = e2->left.sym;
	if (sym1 != sym2)
		return NULL;
	if (sym1->type != SYMBOL_BOOLEAN && sym1->type != SYMBOL_TRISTATE)
		return NULL;
	if (sym1->type == SYMBOL_TRISTATE) {
		if (e1->token == '=' && e2->token == '=' &&
		    ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
		     (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) {
			// (a='y') || (a='m') -> (a!='n')
			return expr_alloc_comp(T_UNEQUAL, sym1, &symbol_no);
		}
		if (e1->token == '=' && e2->token == '=' &&
		    ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
		     (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) {
			// (a='y') || (a='n') -> (a!='m')
			return expr_alloc_comp(T_UNEQUAL, sym1, &symbol_mod);
		}
		if (e1->token == '=' && e2->token == '=' &&
		    ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
		     (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) {
			// (a='m') || (a='n') -> (a!='y')
			return expr_alloc_comp(T_UNEQUAL, sym1, &symbol_yes);
		}
	}
	if (sym1->type == SYMBOL_BOOLEAN && sym1 == sym2) {
		if ((e1->token == '!' && e1->left.expr->token == T_WORD && e2->token == T_WORD) ||
		    (e2->token == '!' && e2->left.expr->token == T_WORD && e1->token == T_WORD))
			return expr_alloc_symbol(&symbol_yes);
	}

	printf("optimize ");
	print_expr(PRINTD, e1, 0);
	printf(" || ");
	print_expr(PRINTD, e2, 0);
	printf(" ?\n");
	return NULL;
}

struct expr *join_and(struct expr *e1, struct expr *e2)
{
	struct expr *tmp;
	struct symbol *sym1, *sym2;

	if (expr_eq(e1, e2))
		return expr_copy(e1);
	if (e1->token != '=' && e1->token != T_UNEQUAL && e1->token != T_WORD && e1->token != '!')
		return NULL;
	if (e2->token != '=' && e2->token != T_UNEQUAL && e2->token != T_WORD && e2->token != '!')
		return NULL;
	if (e1->token == '!') {
		tmp = e1->left.expr;
		if (tmp->token != '=' && tmp->token != T_UNEQUAL && tmp->token != T_WORD)
			return NULL;
		sym1 = tmp->left.sym;
	} else
		sym1 = e1->left.sym;
	if (e2->token == '!') {
		if (e2->left.expr->token != T_WORD)
			return NULL;
		sym2 = e2->left.expr->left.sym;
	} else
		sym2 = e2->left.sym;
	if (sym1 != sym2)
		return NULL;
	if (sym1->type != SYMBOL_BOOLEAN && sym1->type != SYMBOL_TRISTATE)
		return NULL;

	if ((e1->token == T_WORD && e2->token == '=' && e2->right.sym == &symbol_yes) ||
	    (e2->token == T_WORD && e1->token == '=' && e1->right.sym == &symbol_yes))
		// (a) && (a='y') -> (a='y')
		return expr_alloc_comp('=', sym1, &symbol_yes);

	if ((e1->token == T_WORD && e2->token == T_UNEQUAL && e2->right.sym == &symbol_no) ||
	    (e2->token == T_WORD && e1->token == T_UNEQUAL && e1->right.sym == &symbol_no))
		// (a) && (a!='n') -> (a)
		return expr_alloc_symbol(sym1);

	if (sym1->type == SYMBOL_TRISTATE) {
		if (e1->token == '=' && e2->token == T_UNEQUAL) {
			// (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
			sym2 = e1->right.sym;
			if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
				return sym2 != e2->right.sym ? expr_alloc_comp('=', sym1, sym2)
							     : expr_alloc_symbol(&symbol_no);
		}
		if (e1->token == T_UNEQUAL && e2->token == '=') {
			// (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
			sym2 = e2->right.sym;
			if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
				return sym2 != e1->right.sym ? expr_alloc_comp('=', sym1, sym2)
							     : expr_alloc_symbol(&symbol_no);
		}
		if (e1->token == T_UNEQUAL && e2->token == T_UNEQUAL &&
			   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
			    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes)))
			// (a!='y') && (a!='n') -> (a='m')
			return expr_alloc_comp('=', sym1, &symbol_mod);

		if (e1->token == T_UNEQUAL && e2->token == T_UNEQUAL &&
			   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
			    (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes)))
			// (a!='y') && (a!='m') -> (a='n')
			return expr_alloc_comp('=', sym1, &symbol_no);

		if (e1->token == T_UNEQUAL && e2->token == T_UNEQUAL &&
			   ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
			    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod)))
			// (a!='m') && (a!='n') -> (a='m')
			return expr_alloc_comp('=', sym1, &symbol_yes);

		if ((e1->token == T_WORD && e2->token == '=' && e2->right.sym == &symbol_mod) ||
		    (e2->token == T_WORD && e1->token == '=' && e1->right.sym == &symbol_mod) ||
		    (e1->token == T_WORD && e2->token == T_UNEQUAL && e2->right.sym == &symbol_yes) ||
		    (e2->token == T_WORD && e1->token == T_UNEQUAL && e1->right.sym == &symbol_yes))
			return NULL;
	}
	printf("optimize ");
	print_expr(PRINTD, e1, 0);
	printf(" && ");
	print_expr(PRINTD, e2, 0);
	printf(" ?\n");
	return NULL;
}

void eliminate_dups1(int token, struct expr **ep1, struct expr **ep2)
{
#define e1 (*ep1)
#define e2 (*ep2)
	struct expr *tmp;

	if (e1->token == token) {
		eliminate_dups1(token, &e1->left.expr, &e2);
		eliminate_dups1(token, &e1->right.expr, &e2);
		return;
	}
	if (e2->token == token) {
		eliminate_dups1(token, &e1, &e2->left.expr);
		eliminate_dups1(token, &e1, &e2->right.expr);
		return;
	}
	if (e1 == e2)
		return;

	switch (e1->token) {
	case T_OR: case T_AND:
		eliminate_dups1(e1->token, &e1, &e1);
	}

	switch (token) {
	case T_OR:
		tmp = join_or(e1, e2);
		if (tmp) {
			expr_free(e1); expr_free(e2);
			e1 = expr_alloc_symbol(&symbol_no);
			e2 = tmp;
			trans_count++;
		}
		break;
	case T_AND:
		tmp = join_and(e1, e2);
		if (tmp) {
			expr_free(e1); expr_free(e2);
			e1 = expr_alloc_symbol(&symbol_yes);
			e2 = tmp;
			trans_count++;
		}
		break;
	}
#undef e1
#undef e2
}

void eliminate_dups2(int token, struct expr **ep1, struct expr **ep2)
{
#define e1 (*ep1)
#define e2 (*ep2)
	struct expr *tmp, *tmp1, *tmp2;

	if (e1->token == token) {
		eliminate_dups2(token, &e1->left.expr, &e2);
		eliminate_dups2(token, &e1->right.expr, &e2);
		return;
	}
	if (e2->token == token) {
		eliminate_dups2(token, &e1, &e2->left.expr);
		eliminate_dups2(token, &e1, &e2->right.expr);
	}
	if (e1 == e2)
		return;

	switch (e1->token) {
	case T_OR:
		eliminate_dups2(e1->token, &e1, &e1);
		// (FOO || BAR) && (!FOO && !BAR) -> n
		tmp1 = trans_expr(expr_alloc_one('!', expr_copy(e1)));
		tmp2 = expr_copy(e2);
		tmp = extract_eq_and(&tmp1, &tmp2);
		if (expr_is_yes(tmp1)) {
			expr_free(e1);
			e1 = expr_alloc_symbol(&symbol_no);
			trans_count++;
		}
		expr_free(tmp2);
		expr_free(tmp1);
		expr_free(tmp);
		break;
	case T_AND:
		eliminate_dups2(e1->token, &e1, &e1);
		// (FOO && BAR) || (!FOO || !BAR) -> y
		tmp1 = trans_expr(expr_alloc_one('!', expr_copy(e1)));
		tmp2 = expr_copy(e2);
		tmp = extract_eq_or(&tmp1, &tmp2);
		if (expr_is_no(tmp1)) {
			expr_free(e1);
			e1 = expr_alloc_symbol(&symbol_yes);
			trans_count++;
		}
		expr_free(tmp2);
		expr_free(tmp1);
		expr_free(tmp);
		break;
	}
#undef e1
#undef e2
}

struct expr *eliminate_dups(struct expr *e)
{
	int oldcount;
	if (!e)
		return e;

	oldcount = trans_count;
	while (1) {
		trans_count = 0;
		switch (e->token) {
		case T_OR: case T_AND:
			eliminate_dups1(e->token, &e, &e);
			eliminate_dups2(e->token, &e, &e);
		}
		if (!trans_count)
			break;
		e = eliminate_yn(e);
	}
	trans_count = oldcount;
	return e;
}

struct expr *eliminate_yn(struct expr *e)
{
	struct expr *tmp;

	if (e) switch (e->token) {
	case T_AND:
		e->left.expr = eliminate_yn(e->left.expr);
		e->right.expr = eliminate_yn(e->right.expr);
		if (e->left.expr->token == T_WORD) {
			if (e->left.expr->left.sym == &symbol_no) {
				expr_free(e->left.expr);
				expr_free(e->right.expr);
				e->token = T_WORD;
				e->left.sym = &symbol_no;
				e->right.expr = NULL;
				return e;
			} else if (e->left.expr->left.sym == &symbol_yes) {
				free(e->left.expr);
				tmp = e->right.expr;
				*e = *(e->right.expr);
				free(tmp);
				return e;
			}
		}
		if (e->right.expr->token == T_WORD) {
			if (e->right.expr->left.sym == &symbol_no) {
				expr_free(e->left.expr);
				expr_free(e->right.expr);
				e->token = T_WORD;
				e->left.sym = &symbol_no;
				e->right.expr = NULL;
				return e;
			} else if (e->right.expr->left.sym == &symbol_yes) {
				free(e->right.expr);
				tmp = e->left.expr;
				*e = *(e->left.expr);
				free(tmp);
				return e;
			}
		}
		break;
	case T_OR:
		e->left.expr = eliminate_yn(e->left.expr);
		e->right.expr = eliminate_yn(e->right.expr);
		if (e->left.expr->token == T_WORD) {
			if (e->left.expr->left.sym == &symbol_no) {
				free(e->left.expr);
				tmp = e->right.expr;
				*e = *(e->right.expr);
				free(tmp);
				return e;
			} else if (e->left.expr->left.sym == &symbol_yes) {
				expr_free(e->left.expr);
				expr_free(e->right.expr);
				e->token = T_WORD;
				e->left.sym = &symbol_yes;
				e->right.expr = NULL;
				return e;
			}
		}
		if (e->right.expr->token == T_WORD) {
			if (e->right.expr->left.sym == &symbol_no) {
				free(e->right.expr);
				tmp = e->left.expr;
				*e = *(e->left.expr);
				free(tmp);
				return e;
			} else if (e->right.expr->left.sym == &symbol_yes) {
				expr_free(e->left.expr);
				expr_free(e->right.expr);
				e->token = T_WORD;
				e->left.sym = &symbol_yes;
				e->right.expr = NULL;
				return e;
			}
		}
		break;
	}
	return e;
}

struct expr *trans_expr(struct expr *e)
{
	struct expr *tmp;

	if (!e)
		return NULL;
	switch (e->token) {
	case '=':
	case T_UNEQUAL:
	case T_WORD:
	case '^':
		break;
	default:
		e->left.expr = trans_expr(e->left.expr);
		e->right.expr = trans_expr(e->right.expr);
	}

	switch (e->token) {
	case '=':
		if (e->left.sym->type != SYMBOL_BOOLEAN)
			break;
		if (e->right.sym == &symbol_no) {
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, " -> ");
			e->token = '!';
			e->left.expr = expr_alloc_symbol(e->left.sym);
			e->right.sym = NULL;
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, "\n");
			break;
		}
		if (e->right.sym == &symbol_mod) {
			printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
			e->token = T_WORD;
			e->left.sym = &symbol_no;
			e->right.sym = NULL;
			break;
		}
		if (e->right.sym == &symbol_yes) {
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, " -> ");
			e->token = T_WORD;
			e->right.sym = NULL;
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, "\n");
			break;
		}
		break;
	case T_UNEQUAL:
		if (e->left.sym->type != SYMBOL_BOOLEAN)
			break;
		if (e->right.sym == &symbol_no) {
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, " -> ");
			e->token = T_WORD;
			e->right.sym = NULL;
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, "\n");
			break;
		}
		if (e->right.sym == &symbol_mod) {
			printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
			e->token = T_WORD;
			e->left.sym = &symbol_yes;
			e->right.sym = NULL;
			break;
		}
		if (e->right.sym == &symbol_yes) {
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, " -> ");
			e->token = '!';
			e->left.expr = expr_alloc_symbol(e->left.sym);
			e->right.sym = NULL;
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, "\n");
			break;
		}
		break;
	case '!':
		switch (e->left.expr->token) {
		case '!':
			// !!a -> a
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, " -> ");
			tmp = e->left.expr->left.expr;
			free(e->left.expr);
			free(e);
			e = tmp;
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, "\n");
			e = trans_expr(e);
			break;
		case '=':
		case T_UNEQUAL:
			// !a='x' -> a!='x'
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, " -> ");
			tmp = e->left.expr;
			free(e);
			e = tmp;
			if (e->token == '=')
				e->token = T_UNEQUAL;
			else
				e->token = '=';
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, "\n");
			break;
		case T_OR:
			// !(a || b) -> !a && !b
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, " -> ");
			tmp = e->left.expr;
			e->token = T_AND;
			e->right.expr = expr_alloc_one('!', tmp->right.expr);
			tmp->token = '!';
			tmp->right.expr = NULL;
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, "\n");
			e = trans_expr(e);
			break;
		case T_AND:
			// !(a && b) -> !a || !b
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, " -> ");
			tmp = e->left.expr;
			e->token = T_OR;
			e->right.expr = expr_alloc_one('!', tmp->right.expr);
			tmp->token = '!';
			tmp->right.expr = NULL;
			print_expr(DEBUG_OPT2, e, 0);
			printd(DEBUG_OPT2, "\n");
			e = trans_expr(e);
			break;
		case T_WORD:
			if (e->left.expr->left.sym == &symbol_yes) {
				// !'y' -> 'n'
				print_expr(DEBUG_OPT2, e, 0);
				printd(DEBUG_OPT2, " -> ");
				tmp = e->left.expr;
				free(e);
				e = tmp;
				e->token = T_WORD;
				e->left.sym = &symbol_no;
				print_expr(DEBUG_OPT2, e, 0);
				printd(DEBUG_OPT2, "\n");
				break;
			}
			if (e->left.expr->left.sym == &symbol_no) {
				// !'n' -> 'y'
				print_expr(DEBUG_OPT2, e, 0);
				printd(DEBUG_OPT2, " -> ");
				tmp = e->left.expr;
				free(e);
				e = tmp;
				e->token = T_WORD;
				e->left.sym = &symbol_yes;
				print_expr(DEBUG_OPT2, e, 0);
				printd(DEBUG_OPT2, "\n");
				break;
			}
			break;
		}
		break;
	}
	return e;
}

struct expr *extract_eq_and(struct expr **ep1, struct expr **ep2)
{
	struct expr *tmp = NULL;
	extract_eq(T_AND, &tmp, ep1, ep2);
	if (tmp) {
		*ep1 = eliminate_yn(*ep1);
		*ep2 = eliminate_yn(*ep2);
	}
	return tmp;
}

struct expr *extract_eq_or(struct expr **ep1, struct expr **ep2)
{
	struct expr *tmp = NULL;
	extract_eq(T_OR, &tmp, ep1, ep2);
	if (tmp) {
		*ep1 = eliminate_yn(*ep1);
		*ep2 = eliminate_yn(*ep2);
	}
	return tmp;
}

void extract_eq(int token, struct expr **ep, struct expr **ep1, struct expr **ep2)
{
#define e1 (*ep1)
#define e2 (*ep2)
	if (e1->token == token) {
		extract_eq(token, ep, &e1->left.expr, &e2);
		extract_eq(token, ep, &e1->right.expr, &e2);
		return;
	}
	if (e2->token == token) {
		extract_eq(token, ep, ep1, &e2->left.expr);
		extract_eq(token, ep, ep1, &e2->right.expr);
		return;
	}
	if (expr_eq(e1, e2)) {
		*ep = *ep ? expr_alloc_two(token, *ep, e1) : e1;
		expr_free(e2);
		if (token == T_AND) {
			e1 = expr_alloc_symbol(&symbol_yes);
			e2 = expr_alloc_symbol(&symbol_yes);
		} else if (token == T_OR) {
			e1 = expr_alloc_symbol(&symbol_no);
			e2 = expr_alloc_symbol(&symbol_no);
		}
	}
#undef e1
#undef e2
}

struct expr *optimize_expr_or(struct expr *e)
{
	struct expr *tmp;

	if (!e || e->token != T_OR)
		return e;

	e->left.expr = optimize_expr_or(e->left.expr);
	e->right.expr = optimize_expr_or(e->right.expr);

	tmp = extract_eq_and(&e->left.expr, &e->right.expr);
	if (!tmp)
		return e;
	return expr_alloc_two(T_AND, tmp, e);
}

int contains_expr(struct expr *dep, struct expr *e)
{
	if (!dep)
		return 0;
	if (dep->token == T_AND)
		return contains_expr(dep->left.expr, e) || contains_expr(dep->right.expr, e);
	return expr_eq(dep, e);
}

struct expr *trans_comp_expr(struct expr *e, int token, struct symbol *sym)
{
	struct expr *e1, *e2;

	switch (e->token) {
	case T_AND:
		e1 = trans_comp_expr(e->left.expr, '=', sym);
		e2 = trans_comp_expr(e->right.expr, '=', sym);
		if (sym == &symbol_yes)
			e = expr_alloc_two(T_AND, e1, e2);
		if (sym == &symbol_no)
			e = expr_alloc_two(T_OR, e1, e2);
		if (token == T_UNEQUAL)
			e = expr_alloc_one('!', e);
		return e;
	case T_OR:
		e1 = trans_comp_expr(e->left.expr, '=', sym);
		e2 = trans_comp_expr(e->right.expr, '=', sym);
		if (sym == &symbol_yes)
			e = expr_alloc_two(T_OR, e1, e2);
		if (sym == &symbol_no)
			e = expr_alloc_two(T_AND, e1, e2);
		if (token == T_UNEQUAL)
			e = expr_alloc_one('!', e);
		return e;
	case '!':
		switch (token) {
		case '=':
			return trans_comp_expr(e->left.expr, T_UNEQUAL, sym);
		case T_UNEQUAL:
			return trans_comp_expr(e->left.expr, '=', sym);
		}
		break;
	case T_UNEQUAL:
	case '=':
		if (token == '=') {
			if (sym == &symbol_yes)
				return expr_copy(e);
			if (sym == &symbol_mod)
				return expr_alloc_symbol(&symbol_no);
			if (sym == &symbol_no)
				return expr_alloc_one('!', expr_copy(e));
		} else {
			if (sym == &symbol_yes)
				return expr_alloc_one('!', expr_copy(e));
			if (sym == &symbol_mod)
				return expr_alloc_symbol(&symbol_yes);
			if (sym == &symbol_no)
				return expr_copy(e);
		}
		break;
	case T_WORD:
		return expr_alloc_comp(token, e->left.sym, sym);
	}
	return NULL;
}

void eliminate_dep(struct symbol *sym, struct expr *dep)
{
	struct symbol *sym2;
	struct expr *dep2;

	if (dep->token == T_AND) {
		eliminate_dep(sym, dep->left.expr);
		eliminate_dep(sym, dep->right.expr);
	}
#if 0
	if (dep->token == T_WORD ||
	    (sym->type == SYMBOL_BOOLEAN &&
	     ((dep->token == T_UNEQUAL && dep->right.sym == &symbol_no) ||
	      (dep->token == '=' && dep->right.sym == &symbol_yes)))) {
		sym2 = dep->left.sym;
		if (sym2->dep) {
			if (!sym2->dep2)
				eliminate_dep(sym2, sym2->dep);
			sym->dep2 = !sym->dep2 ? expr_copy(sym2->dep2)
				: expr_alloc_two(T_AND, expr_copy(sym2->dep2), sym->dep2);
		}
	}
#else
	switch (dep->token) {
	case T_WORD:
	case T_UNEQUAL:
	case '=':
		sym2 = dep->left.sym;
		if (sym2->type != SYMBOL_BOOLEAN && sym2->type != SYMBOL_TRISTATE)
			break;
		if (!sym2->dep)
			break;
		if (!sym2->dep2)
			eliminate_dep(sym2, sym2->dep);
		if (dep->token == T_WORD) {
			if (sym2->type == SYMBOL_BOOLEAN)
				dep2 = trans_comp_expr(sym2->dep2, T_UNEQUAL, &symbol_no);
			else
				dep2 = expr_copy(sym2->dep2);
		} else {
			//if ((dep->token == T_UNEQUAL && dep->right.sym == &symbol_no) ||
			//    (sym2->type == SYMBOL_BOOLEAN &&
			//     dep->token == '=' && dep->right.sym == &symbol_yes))
			if ((dep->token == '=' && dep->right.sym == &symbol_yes) ||
			    (sym2->type == SYMBOL_BOOLEAN &&
			     dep->token == T_UNEQUAL && dep->right.sym == &symbol_no))
				dep2 = trans_comp_expr(sym2->dep2, dep->token, dep->right.sym);
			else
				break;
		}
		dep2 = eliminate_yn(trans_expr(fixup_expr(dep2)));
		sym->dep2 = !sym->dep2 ? dep2 : expr_alloc_two(T_AND, dep2, sym->dep2);
	}
#endif
	if (sym->dep == dep) {
		printd(DEBUG_DEP, "el: %s (", sym->name);
		print_expr(DEBUG_DEP, sym->dep, 0);
		printd(DEBUG_DEP, ")\n");
		if (sym->dep2) {
			print_expr(DEBUG_DEP, sym->dep2, 0); printd(DEBUG_DEP, "\n");
			sym->dep2 = eliminate_yn(eliminate_dups(sym->dep2));
			switch (sym->type) {
			case SYMBOL_BOOLEAN: case SYMBOL_INT: case SYMBOL_HEX: case SYMBOL_STRING:
				sym->dep2 = trans_bool_expr(sym->dep2);
				break;
			}
			print_expr(DEBUG_DEP, sym->dep2, 0); printd(DEBUG_DEP, "\n");
			sym->dep = expr_alloc_two(T_AND, expr_copy(sym->dep2), sym->dep);
			sym->dep = eliminate_dups(sym->dep);
			print_expr(DEBUG_DEP, sym->dep, 0); printd(DEBUG_DEP, "\n");
			dep = expr_copy(sym->dep2);
			eliminate_eq(T_AND, &dep, &sym->dep);
			sym->dep = eliminate_yn(sym->dep);
			sym->dep2 = expr_alloc_two(T_AND, sym->dep2, expr_copy(sym->dep));
		} else
			sym->dep2 = expr_copy(sym->dep);
		printd(DEBUG_DEP, " -> ");
		print_expr(DEBUG_DEP, sym->dep2, 0);
		printd(DEBUG_DEP, " -> ");
		print_expr(DEBUG_DEP, sym->dep, 0);
		printd(DEBUG_DEP, "\n");
	}
}

struct symbol * dep_check_symbol(struct symbol *sym);

struct symbol *dep_check_expr(struct expr *e)
{
	struct symbol *sym;
	if (!e)
		return 0;
	switch (e->token) {
	case T_WORD:
	case T_WORD_QUOTE:
	case T_WORD_DQUOTE:
		return dep_check_symbol(e->left.sym);
	case '=':
	case T_UNEQUAL:
		sym = dep_check_symbol(e->left.sym);
		if (sym)
			return sym;
		return dep_check_symbol(e->right.sym);
	case T_OR:
	case T_AND:
		sym = dep_check_expr(e->left.expr);
		if (sym)
			return sym;
		return dep_check_expr(e->right.expr);
	case '!':
		return dep_check_expr(e->left.expr);
	}
	printf("how to check %d?\n", e->token);
	return NULL;
}

struct symbol *dep_check_symbol(struct symbol *sym)
{
	struct statement *stmt, *stmt2;
	struct symbol *sym2;
	struct expr *e1, *e2;

	if (sym->flags & SYMBOL_CHECK) {
		printf("recursive dependency: %s", sym->name);
		return sym;
	}

	sym->flags |= SYMBOL_CHECK;
	sym2 = dep_check_expr(sym->dep);
	if (sym2) {
		printf(" %s", sym->name);
		if (sym->type != sym2->type)
			goto out;
		switch (sym->type) {
		case SYMBOL_TRISTATE:
			e1 = expr_alloc_comp(T_UNEQUAL, sym, &symbol_yes);
			e2 = expr_alloc_comp(T_UNEQUAL, sym2, &symbol_yes);
			break;
		case SYMBOL_BOOLEAN:
			e1 = expr_alloc_one('!', expr_alloc_symbol(sym));
			e2 = expr_alloc_one('!', expr_alloc_symbol(sym2));
			break;
		default:
			goto out;
		}
		if (contains_expr(sym->dep, e2) && contains_expr(sym2->dep, e1)) {
			printf(" (choice(%d) detected)", sym->type);
			sym->flags |= SYMBOL_CHOICE;
			eliminate_eq(T_AND, &sym->dep, &e2);
			sym->dep = eliminate_yn(sym->dep);
			sym2->flags |= SYMBOL_CHOICE;
			eliminate_eq(T_AND, &sym2->dep, &e1);
			sym2->dep = eliminate_yn(sym2->dep);
			stmt = malloc(sizeof(*stmt));
			memset(stmt, 0, sizeof(*stmt));
			stmt->token = '^';
			stmt->text = "change me!";
			stmt->dep = expr_alloc_one('^', NULL);
			stmt->dep->right.sym = sym;
			stmt->dep = expr_alloc_one('^', stmt->dep);
			stmt->dep->right.sym = sym2;
			stmt->dep = expr_alloc_one('^', stmt->dep);
			stmt->dep->right.sym = &symbol_no;
			for (stmt2 = sym->stmt; stmt2->next; stmt2 = stmt2->next)
				;
			if (stmt2->token == '^')
				printf("warning: symbol %s has dup choice statement?\n", sym->name);
			else
				stmt2->next = stmt;
			for (stmt2 = sym2->stmt; stmt2->next; stmt2 = stmt2->next)
				;
			if (stmt2->token == '^')
				printf("warning: symbol %s has dup choice statement?\n", sym->name);
			else
				stmt2->next = stmt;
		}
		expr_free(e1);
		expr_free(e2);
	}
	if (!sym2) for (stmt = sym->stmt; stmt; stmt = stmt->next) {
		if (!stmt->token || stmt->token == '^')
			continue;
		sym2 = dep_check_expr(stmt->dep);
		if (sym2) {
			printf(" %s", sym->name);
			break;
		}
	}
out:
	sym->flags &= ~SYMBOL_CHECK;

	return sym2;
}

const char *sym_typename(int type)
{
	switch (type) {
	case SYMBOL_UNKNOWN: return "unknown";
	case SYMBOL_BOOLEAN: return "bool";
	case SYMBOL_TRISTATE: return "tristate";
	case SYMBOL_INT: return "integer";
	case SYMBOL_HEX: return "hex";
	case SYMBOL_STRING: return "string";
	case SYMBOL_OTHER: return "other";
	default: return "???";
	}
}

void optimize_config(void)
{
	struct symbol *sym;
	struct statement *stmt, *stmt2, **stmtp;
	struct expr *e;
	int i;

	for_all_symbols(i, sym) {
	again:
		for (stmt = sym->stmt; stmt; stmt = stmt->next) {
			switch (stmt->token) {
			case '^':
			case T_BOOL:
			case T_DEP_BOOL:
			case T_DEP_MBOOL:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_BOOLEAN;
				if (sym->type != SYMBOL_BOOLEAN)
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s)\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_BOOLEAN));
				if (sym->type == SYMBOL_TRISTATE) {
					printf("%s:%d:Force stmt to dep_tristate\n",
						stmt->file->name, stmt->lineno);
					stmt->token = T_DEP_TRISTATE;
				}
				break;
			case T_DEFINE_BOOL:
				if (stmt->def == &symbol_mod) {
					printf("%s:%d:warning: symbol %s set to 'm', force stmt to define_tristate\n",
						stmt->file->name, stmt->lineno, sym->name);
					stmt->token = T_DEFINE_TRISTATE;
				}
				break;
			case T_HEX:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_HEX;
				if (sym->type != SYMBOL_HEX)
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s)\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_HEX));
				break;
			case T_INT:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_INT;
				if (sym->type != SYMBOL_INT)
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s)\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_INT));
				break;
			case T_STRING:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_STRING;
				if (sym->type != SYMBOL_STRING)
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s)\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_STRING));
				break;
			case T_TRISTATE:
			case T_DEP_TRISTATE:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_TRISTATE;
				if (sym->type != SYMBOL_TRISTATE) {
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s), force symbol to tristate\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_TRISTATE));
					sym->type = SYMBOL_TRISTATE;
					goto again;
				}
				break;
			}
		}
		for (stmt = sym->stmt; stmt; stmt = stmt->next) {
			switch (stmt->token) {
			case T_DEFINE_BOOL:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_BOOLEAN;
				if (sym->type != SYMBOL_BOOLEAN)
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s)\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_BOOLEAN));
				if (sym->type == SYMBOL_TRISTATE) {
					printf("%s:%d:Force stmt to define_tristate\n",
						stmt->file->name, stmt->lineno);
					stmt->token = T_DEFINE_TRISTATE;
				}
				break;
			case T_DEFINE_TRISTATE:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_TRISTATE;
				if (sym->type != SYMBOL_TRISTATE) {
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s), force symbol to tristate\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_TRISTATE));
					sym->type = SYMBOL_TRISTATE;
					goto again;
				}
				break;
			case T_DEFINE_HEX:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_HEX;
				if (sym->type != SYMBOL_HEX)
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s)\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_HEX));
				break;
			case T_DEFINE_INT:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_INT;
				if (sym->type != SYMBOL_INT)
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s)\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_INT));
				break;
			case T_DEFINE_STRING:
				if (sym->type == SYMBOL_UNKNOWN)
					sym->type = SYMBOL_STRING;
				if (sym->type != SYMBOL_STRING)
					printf("%s:%d:warning: symbol %s has conflicting types (%s,%s)\n",
						stmt->file->name, stmt->lineno, sym->name,
						sym_typename(sym->type), sym_typename(SYMBOL_STRING));
				break;
			}
		}
	}
	for_all_symbols(i, sym) {
		printd(DEBUG_OPT1, "o1: %s\n", sym->name);
		for (stmt = sym->stmt; stmt; stmt = stmt->next) {
			print_expr(DEBUG_OPT1, stmt->dep, 0);
			printd(DEBUG_OPT1, " & ");
			print_expr(DEBUG_OPT1, stmt->dep2, 0);
			printd(DEBUG_OPT1, "\n");
			if (stmt->dep || stmt->dep2) {
				struct expr *e = stmt->dep2;
				if (stmt->token == T_DEP_BOOL) {
					e = trans_dep_bool(e);
					stmt->token = T_DEP_MBOOL;
				}
				if (stmt->dep && e) {
					stmt->dep = expr_alloc_two(T_AND, stmt->dep, e);
				} else if (e)
					stmt->dep = e;
				stmt->dep2 = NULL;
				switch (stmt->token) {
				case T_DEP_MBOOL:	stmt->token = T_BOOL; break;
				case T_DEP_TRISTATE:	stmt->token = T_TRISTATE; break;
				}
			}
			stmt->dep = fixup_expr(stmt->dep);
			print_expr(DEBUG_OPT1, stmt->dep, 0);
			printd(DEBUG_OPT1, "\n");
		}
	}
	for_all_symbols(i, sym) {
		printd(DEBUG_OPT3, "o3: %s\n", sym->name);
		for (stmt = sym->stmt; stmt; stmt = stmt->next) {
			print_expr(DEBUG_OPT3, stmt->dep, 0);
			printd(DEBUG_OPT3, "\n");
			stmt->dep = eliminate_dups(trans_expr(stmt->dep));
			switch (sym->type) {
			case SYMBOL_BOOLEAN: case SYMBOL_INT: case SYMBOL_HEX: case SYMBOL_STRING:
				stmt->dep = trans_bool_expr(stmt->dep);
				break;
			}
			print_expr(DEBUG_OPT3, stmt->dep, 0);
			printd(DEBUG_OPT3, "\n");
		}
	}
	for_all_symbols(i, sym) {
		sym->dep = eliminate_yn(sym->dep);
		for (stmt = sym->stmt; stmt; stmt = stmt->next) {
			stmt->dep = eliminate_yn(stmt->dep);
			if (expr_is_no(stmt->dep)) {
				printf("%s:%d: stmt can never be reached, removed!\n", stmt->file->name, stmt->lineno);
				stmt->token = 0;
			}
		}
	}
	for_all_symbols(i, sym) {
		stmtp = &sym->stmt;
		stmt = *stmtp;
		if (!stmt) {
			printf("undefined symbol %s\n", sym->name);
			continue;
		}
		//if (sym->flags & SYMBOL_CHOICE)
		//	continue;
		while (stmt) {
			if (!stmt->token || stmt->token == '^') {
				stmtp = &stmt->next;
				stmt = *stmtp;
				continue;
			}
#if 1
			if ((stmt->token == T_DEFINE_BOOL || stmt->token == T_DEFINE_TRISTATE) &&
			    (stmt->def == &symbol_no || expr_is_no(stmt->dep))) {
				stmt->token = 0;
				stmt->def = NULL;
				expr_free(stmt->dep);
				stmt->dep = NULL;
				continue;
			}
#endif
			//if (stmt->text && stmt != sym->stmt) {
			//	if (sym->stmt->text)
			//		printf("warning: another stmt with prompt for %s????\n", sym->name);
			//	*stmtp = stmt->next;
			//	stmt->next = sym->stmt;
			//	sym->stmt = stmt;
			//}
			for (stmt2 = stmt->next; stmt2; stmt2 = stmt2->next) {
				if (stmt->token != stmt2->token || stmt->def != stmt2->def ||
				    stmt->menu->parent != stmt2->menu->parent)
					continue;
				if (stmt->text) {
					if (strcmp(skip_space(stmt->text), skip_space(stmt2->text))) {
						printf("warning: prompts differ for %s?\n", sym->name);
						continue;
					}
				}
				if (!stmt->dep)
					stmt->dep = expr_alloc_symbol(&symbol_yes);
				if (!stmt2->dep)
					stmt2->dep = expr_alloc_symbol(&symbol_yes);
				stmt->dep = optimize_expr_or(expr_alloc_two(T_OR, stmt->dep, stmt2->dep));
				stmt2->token = 0;
				free((void *)stmt2->text);
				stmt2->text = NULL;
				stmt2->def = NULL;
				stmt2->dep = NULL;
			}
			if (stmt->text && stmt != sym->stmt) {
				*stmtp = stmt->next;
				stmt->next = sym->stmt;
				sym->stmt = stmt;
			} else
				stmtp = &stmt->next;
			stmt = *stmtp;
		}
		for (stmt = sym->stmt; stmt; stmt = stmt->next) {
			if (!stmt->token || stmt->token == '^')
				continue;
			if (!sym->dep) {
				if (!stmt->dep)
					break;
				sym->dep = stmt->dep;
				stmt->dep = NULL;
			} else {
				e = sym->dep;
				if (stmt->dep) {
					sym->dep = extract_eq_and(&e, &stmt->dep);
					if (expr_is_yes(e)) {
						expr_free(e);
						continue;
					}
				} else
					sym->dep = NULL;
				for (stmt2 = sym->stmt; stmt != stmt2; stmt2 = stmt2->next) {
					if (!stmt2->token || stmt2->token == '^')
						continue;
					stmt2->dep = !stmt2->dep ? expr_copy(e) :
						expr_alloc_two(T_AND, expr_copy(e), stmt2->dep);
				}
				expr_free(e);
				if (!sym->dep)
					break;
			}
		}
#if 0
		for (stmt1 = sym->stmt; stmt1; stmt1 = stmt1->next)
			if (stmt1->token && stmt1->token != '^')
				break;
		if (stmt1 && !expr_is_yes(stmt1->dep)) {
			e = trans_expr(expr_alloc_one('!', stmt1->dep));
			stmt1->dep = NULL;
			for (stmt = stmt1->next; stmt; stmt = stmt->next) {
				if (!stmt->token || stmt->token == '^')
					continue;
				if (stmt->dep) {
					tmp = extract_eq_and(&e, &stmt->dep);
					if (expr_is_yes(e)) {
						expr_free(e);
						e = tmp;
						continue;
					}
					if (tmp) {
						e = expr_alloc_two(T_AND, expr_copy(tmp), e);
						stmt->dep = expr_alloc_two(T_AND, tmp, stmt->dep);
					}
				}
				for (stmt2 = stmt1->next; stmt != stmt2; stmt2 = stmt2->next) {
					if (!stmt2->token || stmt2->token == '^')
						continue;
					stmt2->dep = !stmt2->dep ? expr_copy(e) :
						expr_alloc_two(T_AND, expr_copy(e), stmt2->dep);
				}
				stmt1->dep = trans_expr(expr_alloc_one('!', e));
				break;
			}
			if (!stmt1->dep) {
				e = trans_expr(expr_alloc_one('!', e));
				//sym->dep = sym->dep ? expr_alloc_two(T_AND, sym->dep, e) : e;
				stmt1->dep = e;
			}
		}
		for (stmtp = &sym->stmt; *stmtp; stmtp = &stmt->next) {
			stmt = *stmtp;
			if (!stmt->token || stmt->token == '^')
				continue;
			if (!stmt->dep || stmt->text)
				continue;
			for (stmt2 = stmt->next; stmt2; stmt2 = stmt2->next) {
				trans_count = 0;
				if (!stmt2->dep || stmt2->text)
					continue;
				e = trans_expr(expr_alloc_one('!', expr_copy(stmt->dep)));
				tmp = extract_eq_and(&e, &stmt2->dep);
				if (expr_is_yes(e)) {
					expr_free(e);
					expr_free(tmp);
					continue;
				}
				if (tmp)
					stmt2->dep = expr_alloc_two(T_AND, tmp, stmt2->dep);
				e = trans_expr(expr_alloc_one('!', expr_copy(stmt2->dep)));
				tmp = extract_eq_and(&e, &stmt->dep);
				if (expr_is_yes(e)) {
					expr_free(e);
					expr_free(tmp);
					while (stmt->next != stmt2)
						stmt = stmt->next;
					stmt->next = *stmtp;
					*stmtp = stmt2;
					stmt = stmt->next;
					stmt2 = stmt->next;
					stmt->next = (*stmtp)->next;
					(*stmtp)->next = stmt2;
					continue;
				}
				if (tmp)
					stmt->dep = expr_alloc_two(T_AND, tmp, stmt->dep);
			}
		}
#endif
	}
	for_all_symbols(i, sym) {
		printd(DEBUG_OPT4, "o4: %s\n", sym->name);
		print_expr(DEBUG_OPT4, sym->dep, 0); printd(DEBUG_OPT4, "\n");
		sym->dep = eliminate_dups(sym->dep);
		sym->dep = eliminate_yn(sym->dep);
		print_expr(DEBUG_OPT4, sym->dep, 0); printd(DEBUG_OPT4, "\n");
		for (stmt = sym->stmt; stmt; stmt = stmt->next) {
			print_expr(DEBUG_OPT4, stmt->dep, 0); printd(DEBUG_OPT4, "\n");
			stmt->dep = eliminate_dups(stmt->dep);
			stmt->dep = eliminate_yn(stmt->dep);
			print_expr(DEBUG_OPT4, stmt->dep, 0); printd(DEBUG_OPT4, "\n");
		}
	}
	for_all_symbols(i, sym) {
		if (dep_check_symbol(sym))
			printf("\n");
	}
	for_all_symbols(i, sym) {
		if (sym->dep && !sym->dep2) {
			printd(DEBUG_OPT5, "o5: %s\n", sym->name);
			print_expr(DEBUG_OPT5, sym->dep, 0); printd(DEBUG_OPT5, "\n");
			eliminate_dep(sym, sym->dep);
			print_expr(DEBUG_OPT5, sym->dep, 0); printd(DEBUG_OPT5, "\n");
		}
	}
}

int main(int ac, char **av)
{
	FILE *out = NULL;
	char name[128];
	struct symbol *sym;
	struct statement *stmt;

	if (ac > 2)
		cml1debug = 1;
	helplex("Documentation/Configure.help");
	sprintf(name, "arch/%s/Config.help", av[1]);
	helplex(name);
	sprintf(name, "arch/%s/config.in", av[1]);
	scan_init(name);
	sym = lookup_symbol("CONFIG_ARCH", SYMBOL_STRING);
	stmt = add_stmt(sym, T_DEFINE_STRING, NULL, lookup_symbol(av[1], SYMBOL_OTHER));
	stmt->menu = NULL;
	cml1parse();
	optimize_config();
#if 0
	out = fopen("config.new", "w");
	if (!out) {
		printf("unable to open config.new!\n");
		exit(1);
	}
#endif
	print_files(out, name);

	return 0;
}