1/*
2 * tkcond.c
3 *
4 * Eric Youngdale was the original author of xconfig.
5 * Michael Elizabeth Chastain (mec@shout.net) is the current maintainer.
6 *
7 * This file takes the tokenized statement list and transforms 'if ...'
8 * statements.  For each simple statement, I find all of the 'if' statements
9 * that enclose it, and attach the aggregate conditionals of those 'if'
10 * statements to the cond list of the simple statement.
11 *
12 * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
13 * - Steam-clean this file.  I tested this by generating kconfig.tk for
14 *   every architecture and comparing it character-for-character against
15 *   the output of the old tkparse.
16 *
17 * 07 July 1999, Andrzej M. Krzysztofowicz <ankry@mif.pg.gda.pl>
18 * - kvariables removed; all variables are stored in a single table now
19 * - some elimination of options non-valid for current architecture
20 *   implemented.
21 * - negation (!) eliminated from conditions
22 *
23 * TO DO:
24 * - xconfig is at the end of its life cycle.  Contact <mec@shout.net> if
25 *   you are interested in working on the replacement.
26 */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32#include "tkparse.h"
33
34
35
36/*
37 * Mark variables which are defined anywhere.
38 */
39static void mark_variables( struct kconfig * scfg )
40{
41    struct kconfig * cfg;
42    int i;
43
44    for ( i = 1; i <= max_varnum; i++ )
45	vartable[i].defined = 0;
46    for ( cfg = scfg; cfg != NULL; cfg = cfg->next )
47    {
48	if ( cfg->token == token_bool
49	||   cfg->token == token_choice_item
50	||   cfg->token == token_define_bool
51	||   cfg->token == token_define_hex
52	||   cfg->token == token_define_int
53	||   cfg->token == token_define_string
54	||   cfg->token == token_define_tristate
55	||   cfg->token == token_dep_bool
56	||   cfg->token == token_dep_mbool
57	||   cfg->token == token_dep_tristate
58	||   cfg->token == token_hex
59	||   cfg->token == token_int
60	||   cfg->token == token_string
61	||   cfg->token == token_tristate
62	||   cfg->token == token_unset )
63	{
64	    if ( cfg->nameindex > 0 )	/* paranoid */
65	    {
66		vartable[cfg->nameindex].defined = 1;
67	    }
68	}
69    }
70}
71
72
73
74static void free_cond( struct condition *cond )
75{
76    struct condition *tmp, *tmp1;
77    for ( tmp = cond; tmp; tmp = tmp1 )
78    {
79	tmp1 = tmp->next;
80	free( (void*)tmp );
81    }
82}
83
84
85
86/*
87 * Remove the bang operator from a condition to avoid priority problems.
88 * "!" has different priorities as "test" command argument and in
89 * a tk script.
90 */
91static struct condition * remove_bang( struct condition * condition )
92{
93    struct condition * conda, * condb, * prev = NULL;
94
95    for ( conda = condition; conda; conda = conda->next )
96    {
97	if ( conda->op == op_bang && conda->next &&
98	   ( condb = conda->next->next ) )
99	{
100	    if ( condb->op == op_eq || condb->op == op_neq )
101	    {
102		condb->op = (condb->op == op_eq) ? op_neq : op_eq;
103		conda->op = op_nuked;
104		if ( prev )
105		{
106		    prev->next = conda->next;
107		}
108		else
109		{
110		    condition = conda->next;
111		}
112		conda->next = NULL;
113		free_cond( conda );
114		conda = condb;
115	    }
116	}
117	prev = conda;
118    }
119    return condition;
120}
121
122
123
124/*
125 * Make a new condition chain by joining the current condition stack with
126 * the "&&" operator for glue.
127 */
128static struct condition * join_condition_stack( struct condition * conditions [],
129    int depth )
130{
131    struct condition * cond_list;
132    struct condition * cond_last;
133    int i, is_first = 1;
134
135    cond_list = cond_last = NULL;
136
137    for ( i = 0; i < depth; i++ )
138    {
139	if ( conditions[i]->op == op_false )
140	{
141	    struct condition * cnew;
142
143	    /* It is always false condition */
144	    cnew = malloc( sizeof(*cnew) );
145	    memset( cnew, 0, sizeof(*cnew) );
146	    cnew->op = op_false;
147	    cond_list = cond_last = cnew;
148	    goto join_done;
149	}
150    }
151    for ( i = 0; i < depth; i++ )
152    {
153	struct condition * cond;
154	struct condition * cnew;
155	int add_paren;
156
157	/* omit always true conditions */
158	if ( conditions[i]->op == op_true )
159	    continue;
160
161	/* if i have another condition, add an '&&' operator */
162	if ( !is_first )
163	{
164	    cnew = malloc( sizeof(*cnew) );
165	    memset( cnew, 0, sizeof(*cnew) );
166	    cnew->op = op_and;
167	    cond_last->next = cnew;
168	    cond_last = cnew;
169	}
170
171	if ( conditions[i]->op != op_lparen )
172	{
173	    /* add a '(' */
174	    add_paren = 1;
175	    cnew = malloc( sizeof(*cnew) );
176	    memset( cnew, 0, sizeof(*cnew) );
177	    cnew->op = op_lparen;
178	    if ( cond_last == NULL )
179		{ cond_list = cond_last = cnew; }
180	    else
181		{ cond_last->next = cnew; cond_last = cnew; }
182	}
183	else
184	{
185	    add_paren = 0;
186	}
187
188	/* duplicate the chain */
189	for ( cond = conditions [i]; cond != NULL; cond = cond->next )
190	{
191	    cnew            = malloc( sizeof(*cnew) );
192	    cnew->next      = NULL;
193	    cnew->op        = cond->op;
194	    cnew->str       = cond->str ? strdup( cond->str ) : NULL;
195	    cnew->nameindex = cond->nameindex;
196	    if ( cond_last == NULL )
197		{ cond_list = cond_last = cnew; }
198	    else
199		{ cond_last->next = cnew; cond_last = cnew; }
200	}
201
202	if ( add_paren )
203	{
204	    /* add a ')' */
205	    cnew = malloc( sizeof(*cnew) );
206	    memset( cnew, 0, sizeof(*cnew) );
207	    cnew->op = op_rparen;
208	    cond_last->next = cnew;
209	    cond_last = cnew;
210	}
211	is_first = 0;
212    }
213
214    /*
215     * Remove duplicate conditions.
216     */
217    {
218	struct condition *cond1, *cond1b, *cond1c, *cond1d, *cond1e, *cond1f;
219
220	for ( cond1 = cond_list; cond1 != NULL; cond1 = cond1->next )
221	{
222	    if ( cond1->op == op_lparen )
223	    {
224		cond1b = cond1 ->next; if ( cond1b == NULL ) break;
225		cond1c = cond1b->next; if ( cond1c == NULL ) break;
226		cond1d = cond1c->next; if ( cond1d == NULL ) break;
227		cond1e = cond1d->next; if ( cond1e == NULL ) break;
228		cond1f = cond1e->next; if ( cond1f == NULL ) break;
229
230		if ( cond1b->op == op_variable
231		&& ( cond1c->op == op_eq || cond1c->op == op_neq )
232		&&   cond1d->op == op_constant
233		&&   cond1e->op == op_rparen )
234		{
235		    struct condition *cond2, *cond2b, *cond2c, *cond2d, *cond2e, *cond2f;
236
237		    for ( cond2 = cond1f->next; cond2 != NULL; cond2 = cond2->next )
238		    {
239			if ( cond2->op == op_lparen )
240			{
241			    cond2b = cond2 ->next; if ( cond2b == NULL ) break;
242			    cond2c = cond2b->next; if ( cond2c == NULL ) break;
243			    cond2d = cond2c->next; if ( cond2d == NULL ) break;
244			    cond2e = cond2d->next; if ( cond2e == NULL ) break;
245			    cond2f = cond2e->next;
246
247			    /* look for match */
248			    if ( cond2b->op == op_variable
249			    &&   cond2b->nameindex == cond1b->nameindex
250			    &&   cond2c->op == cond1c->op
251			    &&   cond2d->op == op_constant
252			    &&   strcmp( cond2d->str, cond1d->str ) == 0
253			    &&   cond2e->op == op_rparen )
254			    {
255				/* one of these must be followed by && */
256				if ( cond1f->op == op_and
257				|| ( cond2f != NULL && cond2f->op == op_and ) )
258				{
259				    /* nuke the first duplicate */
260				    cond1 ->op = op_nuked;
261				    cond1b->op = op_nuked;
262				    cond1c->op = op_nuked;
263				    cond1d->op = op_nuked;
264				    cond1e->op = op_nuked;
265				    if ( cond1f->op == op_and )
266					cond1f->op = op_nuked;
267				    else
268					cond2f->op = op_nuked;
269				}
270			    }
271			}
272		    }
273		}
274	    }
275	}
276    }
277
278join_done:
279    return cond_list;
280}
281
282
283
284static char * current_arch = NULL;
285
286/*
287 * Eliminating conditions with ARCH = <not current>.
288 */
289static struct condition *eliminate_other_arch( struct condition *list )
290{
291    struct condition *cond1a = list, *cond1b = NULL, *cond1c = NULL, *cond1d = NULL;
292    if ( current_arch == NULL )
293	current_arch = getenv( "ARCH" );
294    if ( current_arch == NULL )
295    {
296	fprintf( stderr, "error: ARCH undefined\n" );
297	exit( 1 );
298    }
299    if ( cond1a->op == op_variable
300    && ! strcmp( vartable[cond1a->nameindex].name, "ARCH" ) )
301    {
302	cond1b = cond1a->next; if ( cond1b == NULL ) goto done;
303	cond1c = cond1b->next; if ( cond1c == NULL ) goto done;
304	cond1d = cond1c->next;
305	if ( cond1c->op == op_constant && cond1d == NULL )
306	{
307	    if ( (cond1b->op == op_eq && strcmp( cond1c->str, current_arch ))
308	    ||   (cond1b->op == op_neq && ! strcmp( cond1c->str, current_arch )) )
309	    {
310		/* This is for another architecture */
311		cond1a->op = op_false;
312		cond1a->next = NULL;
313		free_cond( cond1b );
314		return cond1a;
315	    }
316	    else if ( (cond1b->op == op_neq && strcmp( cond1c->str, current_arch ))
317		 ||   (cond1b->op == op_eq && ! strcmp( cond1c->str, current_arch )) )
318	    {
319		/* This is for current architecture */
320		cond1a->op = op_true;
321		cond1a->next = NULL;
322		free_cond( cond1b );
323		return cond1a;
324	    }
325	}
326	else if ( cond1c->op == op_constant && cond1d->op == op_or )
327	{
328	    if ( (cond1b->op == op_eq && strcmp( cond1c->str, current_arch ))
329	    ||   (cond1b->op == op_neq && ! strcmp( cond1c->str, current_arch )) )
330	    {
331		/* This is for another architecture */
332		cond1b = cond1d->next;
333		cond1d->next = NULL;
334		free_cond( cond1a );
335		return eliminate_other_arch( cond1b );
336	    }
337	    else if ( (cond1b->op == op_neq && strcmp( cond1c->str, current_arch ))
338		 || (cond1b->op == op_eq && ! strcmp( cond1c->str, current_arch )) )
339	    {
340		/* This is for current architecture */
341		cond1a->op = op_true;
342		cond1a->next = NULL;
343		free_cond( cond1b );
344		return cond1a;
345	    }
346	}
347	else if ( cond1c->op == op_constant && cond1d->op == op_and )
348	{
349	    if ( (cond1b->op == op_eq && strcmp( cond1c->str, current_arch ))
350	    ||   (cond1b->op == op_neq && ! strcmp( cond1c->str, current_arch )) )
351	    {
352		/* This is for another architecture */
353		int l_par = 0;
354
355		for ( cond1c = cond1d->next; cond1c; cond1c = cond1c->next )
356		{
357		    if ( cond1c->op == op_lparen )
358			l_par++;
359		    else if ( cond1c->op == op_rparen )
360			l_par--;
361		    else if ( cond1c->op == op_or && l_par == 0 )
362		    /* Expression too complex - don't touch */
363			return cond1a;
364		    else if ( l_par < 0 )
365		    {
366			fprintf( stderr, "incorrect condition: programming error ?\n" );
367			exit( 1 );
368		    }
369		}
370		cond1a->op = op_false;
371		cond1a->next = NULL;
372		free_cond( cond1b );
373		return cond1a;
374	    }
375	    else if ( (cond1b->op == op_neq && strcmp( cond1c->str, current_arch ))
376		 || (cond1b->op == op_eq && ! strcmp( cond1c->str, current_arch )) )
377	    {
378		/* This is for current architecture */
379		cond1b = cond1d->next;
380		cond1d->next = NULL;
381		free_cond( cond1a );
382		return eliminate_other_arch( cond1b );
383	    }
384	}
385    }
386    if ( cond1a->op == op_variable && ! vartable[cond1a->nameindex].defined )
387    {
388	cond1b = cond1a->next; if ( cond1b == NULL ) goto done;
389	cond1c = cond1b->next; if ( cond1c == NULL ) goto done;
390	cond1d = cond1c->next;
391
392	if ( cond1c->op == op_constant
393	&& ( cond1d == NULL || cond1d->op == op_and ) ) /*???*/
394	{
395	    if ( cond1b->op == op_eq && strcmp( cond1c->str, "" ) )
396	    {
397		cond1a->op = op_false;
398		cond1a->next = NULL;
399		free_cond( cond1b );
400		return cond1a;
401	    }
402	}
403	else if ( cond1c->op == op_constant && cond1d->op == op_or )
404	{
405	    if ( cond1b->op == op_eq && strcmp( cond1c->str, "" ) )
406	    {
407		cond1b = cond1d->next;
408		cond1d->next = NULL;
409		free_cond( cond1a );
410		return eliminate_other_arch( cond1b );
411	    }
412	}
413    }
414done:
415    return list;
416}
417
418
419
420/*
421 * This is the main transformation function.
422 */
423void fix_conditionals( struct kconfig * scfg )
424{
425    struct kconfig * cfg;
426
427    /*
428     * Transform op_variable to op_kvariable.
429     */
430    mark_variables( scfg );
431
432    /*
433     * Walk the statement list, maintaining a stack of current conditions.
434     *   token_if      push its condition onto the stack.
435     *   token_else    invert the condition on the top of the stack.
436     *   token_endif   pop the stack.
437     *
438     * For a simple statement, create a condition chain by joining together
439     * all of the conditions on the stack.
440     */
441    {
442	struct condition * cond_stack [32];
443	int depth = 0;
444	struct kconfig * prev = NULL;
445
446	for ( cfg = scfg; cfg != NULL; cfg = cfg->next )
447	{
448	    int good = 1;
449	    switch ( cfg->token )
450	    {
451	    default:
452		break;
453
454	    case token_if:
455		cond_stack [depth++] =
456		    remove_bang( eliminate_other_arch( cfg->cond ) );
457		cfg->cond = NULL;
458		break;
459
460	    case token_else:
461		{
462		    /*
463		     * Invert the condition chain.
464		     *
465		     * Be careful to transfrom op_or to op_and1, not op_and.
466		     * I will need this later in the code that removes
467		     * duplicate conditions.
468		     */
469		    struct condition * cond;
470
471		    for ( cond  = cond_stack [depth-1];
472			  cond != NULL;
473			  cond  = cond->next )
474		    {
475			switch( cond->op )
476			{
477			default:     break;
478			case op_and: cond->op = op_or;   break;
479			case op_or:  cond->op = op_and1; break;
480			case op_neq: cond->op = op_eq;   break;
481			case op_eq:  cond->op = op_neq;  break;
482			case op_true: cond->op = op_false;break;
483			case op_false:cond->op = op_true; break;
484			}
485		    }
486		}
487		break;
488
489	    case token_fi:
490		--depth;
491		break;
492
493	    case token_bool:
494	    case token_choice_item:
495	    case token_choice_header:
496	    case token_comment:
497	    case token_define_bool:
498	    case token_define_hex:
499	    case token_define_int:
500	    case token_define_string:
501	    case token_define_tristate:
502	    case token_endmenu:
503	    case token_hex:
504	    case token_int:
505	    case token_mainmenu_option:
506	    case token_string:
507	    case token_tristate:
508	    case token_unset:
509		cfg->cond = join_condition_stack( cond_stack, depth );
510		if ( cfg->cond && cfg->cond->op == op_false )
511		{
512		    good = 0;
513		    if ( prev )
514			prev->next = cfg->next;
515		    else
516			scfg = cfg->next;
517		}
518		break;
519
520	    case token_dep_bool:
521	    case token_dep_mbool:
522	    case token_dep_tristate:
523		/*
524		 * Same as the other simple statements, plus an additional
525		 * condition for the dependency.
526		 */
527		if ( cfg->cond )
528		{
529		    cond_stack [depth] = eliminate_other_arch( cfg->cond );
530		    cfg->cond = join_condition_stack( cond_stack, depth+1 );
531		}
532		else
533		{
534		    cfg->cond = join_condition_stack( cond_stack, depth );
535		}
536		if ( cfg->cond && cfg->cond->op == op_false )
537		{
538		    good = 0;
539		    if ( prev )
540			prev->next = cfg->next;
541		    else
542			scfg = cfg->next;
543		}
544		break;
545	    }
546	    if ( good )
547		prev = cfg;
548	}
549    }
550}
551
552
553
554