source: IOIPSL/trunk/tools/tkcond.c @ 16

Last change on this file since 16 was 16, checked in by bellier, 17 years ago

JB: add Id (ommited !)

  • Property svn:keywords set to Id
File size: 13.2 KB
Line 
1/* parser config.in
2 * $Id$
3 * Version 1.0
4 * Eric Youngdale
5 * 10/95
6 *
7 * The general idea here is that we want to parse a config.in file and
8 * from this, we generate a wish script which gives us effectively the
9 * same functionality that the original config.in script provided.
10 *
11 * This task is split roughly into 3 parts.  The first parse is the parse
12 * of the input file itself.  The second part is where we analyze the
13 * #ifdef clauses, and attach a linked list of tokens to each of the
14 * menu items.  In this way, each menu item has a complete list of
15 * dependencies that are used to enable/disable the options.
16 * The third part is to take the configuration database we have build,
17 * and build the actual wish script.
18 *
19 * This file contains the code to further process the conditions from
20 * the "ifdef" clauses.
21 *
22 * The conditions are assumed to be one of the following formats
23 *
24 * simple_condition:= "$VARIABLE" == y/n/m
25 * simple_condition:= "$VARIABLE != y/n/m
26 *
27 * simple_condition -a simple_condition
28 *
29 * If the input condition contains '(' or ')' it would screw us up, but for now
30 * this is not a problem.
31 */
32#include <stdlib.h>
33#include <stdio.h>
34#include <string.h>
35#include "tkparse.h"
36
37
38/*
39 * Walk a condition chain and invert it so that the logical result is
40 * inverted.
41 */
42static void invert_condition(struct condition * cnd)
43{
44  /*
45   * This is simple.  Just walk through the list, and invert
46   * all of the operators.
47   */
48  for(;cnd; cnd = cnd->next)
49    {
50      switch(cnd->op)
51        {
52        case op_and:
53          cnd->op = op_or;
54          break;
55        case op_or:
56          /*
57           * This is not turned into op_and - we need to keep track
58           * of what operators were used here since we have an optimization
59           * later on to remove duplicate conditions, and having
60           * inverted ors in there would make it harder if we did not
61           * distinguish an inverted or from an and we inserted because
62           * of nested ifs.
63           */
64          cnd->op = op_and1;
65          break;
66        case op_neq:
67          cnd->op = op_eq;
68          break;
69        case op_eq:
70          cnd->op = op_neq;
71          break;
72        default:
73          break;
74        }
75    }
76}
77
78/*
79 * Walk a condition chain, and free the memory associated with it.
80 */
81static void free_condition(struct condition * cnd)
82{
83  struct condition * next;
84  for(;cnd; cnd = next)
85    {
86      next = cnd->next;
87
88      if( cnd->variable.str != NULL )
89        free(cnd->variable.str);
90
91      free(cnd);
92    }
93}
94
95/*
96 * Walk all of the conditions, and look for choice values.  Convert
97 * the tokens into something more digestible.
98 */
99void fix_choice_cond()
100{
101  struct condition * cond;
102  struct condition * cond2;
103  struct kconfig * cfg;
104  char tmpbuf[10];
105
106  for(cfg = config;cfg != NULL; cfg = cfg->next)
107    {
108      if( cfg->cond == NULL )
109        {
110          continue;
111        }
112
113      for(cond = cfg->cond; cond != NULL; cond = cond->next)
114        {
115          if( cond->op != op_kvariable )
116            continue;
117
118          if( cond->variable.cfg->tok != tok_choice )
119            continue;
120
121          /*
122           * Look ahead for what we are comparing this to.  There should
123           * be one operator in between.
124           */
125          cond2 = cond->next->next;
126          strcpy(tmpbuf, cond->variable.cfg->label);
127
128          if( strcmp(cond2->variable.str, "y") == 0 )
129            {
130              cond->variable.cfg = cond->variable.cfg->choice_label;
131              cond2->variable.str = strdup(tmpbuf);
132            }
133          else
134            {
135              fprintf(stderr,"Ooops\n");
136              exit(0);
137            }
138        }
139
140    }
141}
142
143/*
144 * Walk the stack of conditions, and clone all of them with "&&" operators
145 * gluing them together.  The conditions from each level of the stack
146 * are wrapped in parenthesis so as to guarantee that the results
147 * are logically correct.
148 */
149struct condition * get_token_cond(struct condition ** cond, int depth)
150{
151  int i;
152  struct condition * newcond;
153  struct condition * tail;
154  struct condition * new;
155  struct condition * ocond;
156  struct kconfig * cfg;
157
158  newcond = tail = NULL;
159  for(i=0; i<depth; i++, cond++)
160    {
161      /*
162       * First insert the left parenthesis
163       */
164      new = (struct condition *) malloc(sizeof(struct condition));
165      memset(new, 0, sizeof(*new));
166      new->op = op_lparen;
167      if( tail == NULL )
168        {
169          newcond = tail = new;
170        }
171      else
172        {
173          tail->next = new;
174          tail = new;
175        }
176
177      /*
178       * Now duplicate the chain.
179       */
180      ocond = *cond;
181      for(;ocond != NULL; ocond = ocond->next)
182        {
183          new = (struct condition *) malloc(sizeof(struct condition));
184          memset(new, 0, sizeof(*new));
185          new->op = ocond->op;
186          if( ocond->variable.str != NULL )
187            {
188              if( ocond->op == op_variable )
189                {
190                  /*
191                   * Search for structure to insert here.
192                   */
193                  for(cfg = config;cfg != NULL; cfg = cfg->next)
194                    {
195                      if( cfg->tok != tok_bool
196                         && cfg->tok != tok_int
197                         && cfg->tok != tok_hex
198                         && cfg->tok != tok_tristate
199                         && cfg->tok != tok_choice
200                         && cfg->tok != tok_dep_tristate)
201                        {
202                          continue;
203                        }
204                      if( strcmp(cfg->optionname, ocond->variable.str) == 0)
205                        {
206                          new->variable.cfg = cfg;
207                          new->op = op_kvariable;
208                          break;
209                        }
210                    }
211                  if( cfg == NULL )
212                    {
213                      new->variable.str = strdup(ocond->variable.str);
214                    }
215                }
216              else
217                {
218                  new->variable.str = strdup(ocond->variable.str);
219                }
220            }
221          tail->next = new;
222          tail = new;
223        }
224
225      /*
226       * Next insert the left parenthesis
227       */
228      new = (struct condition *) malloc(sizeof(struct condition));
229      memset(new, 0, sizeof(*new));
230      new->op = op_rparen;
231      tail->next = new;
232      tail = new;
233
234      /*
235       * Insert an and operator, if we have another condition.
236       */
237      if( i < depth - 1 )
238        {
239          new = (struct condition *) malloc(sizeof(struct condition));
240          memset(new, 0, sizeof(*new));
241          new->op = op_and;
242          tail->next = new;
243          tail = new;
244        }
245
246    }
247
248  return newcond;
249}
250
251/*
252 * Walk a single chain of conditions and clone it.  These are assumed
253 * to be created/processed by  get_token_cond in a previous pass.
254 */
255struct condition * get_token_cond_frag(struct condition * cond, 
256                                       struct condition ** last)
257{
258  struct condition * newcond;
259  struct condition * tail;
260  struct condition * new;
261  struct condition * ocond;
262
263  newcond = tail = NULL;
264
265  /*
266   * Now duplicate the chain.
267   */
268  for(ocond = cond;ocond != NULL; ocond = ocond->next)
269    {
270      new = (struct condition *) malloc(sizeof(struct condition));
271      memset(new, 0, sizeof(*new));
272      new->op = ocond->op;
273      new->variable.cfg = ocond->variable.cfg;
274      if( tail == NULL )
275        {
276          newcond = tail = new;
277        }
278      else
279        {
280          tail->next = new;
281          tail = new;
282        }
283    }
284
285  new = (struct condition *) malloc(sizeof(struct condition));
286  memset(new, 0, sizeof(*new));
287  new->op = op_and;
288  tail->next = new;
289  tail = new;
290 
291  *last = tail;
292  return newcond;
293}
294
295/*
296 * Walk through the if conditionals and maintain a chain.
297 */
298void fix_conditionals(struct kconfig * scfg)
299{
300  int depth = 0;
301  int i;
302  struct kconfig * cfg;
303  struct kconfig * cfg1;
304  struct condition * conditions[25];
305  struct condition * cnd;
306  struct condition * cnd1;
307  struct condition * cnd2;
308  struct condition * cnd3;
309  struct condition * newcond;
310  struct condition * last;
311
312  /*
313   * Start by walking the chain.  Every time we see an ifdef, push
314   * the condition chain on the stack.  When we see an "else", we invert
315   * the condition at the top of the stack, and when we see an "endif"
316   * we free all of the memory for the condition at the top of the stack
317   * and remove the condition from the top of the stack.
318   *
319   * For any other type of token (i.e. a bool), we clone a new condition chain
320   * by anding together all of the conditions that are currently stored on
321   * the stack.  In this way, we have a correct representation of whatever
322   * conditions govern the usage of each option.
323   */
324  memset(conditions, 0, sizeof(conditions));
325  for(cfg=scfg;cfg != NULL; cfg = cfg->next)
326    {
327      switch(cfg->tok)
328        {
329        case tok_if:
330          /*
331           * Push this condition on the stack, and nuke the token
332           * representing the ifdef, since we no longer need it.
333           */
334          conditions[depth] = cfg->cond;
335          depth++;
336          cfg->tok = tok_nop;
337          cfg->cond =  NULL;
338          break;
339        case tok_else:
340          /*
341           * For an else, we just invert the condition at the top of
342           * the stack.  This is done in place with no reallocation
343           * of memory taking place.
344           */
345          invert_condition(conditions[depth-1]);
346          cfg->tok = tok_nop;
347          break;
348        case tok_fi:
349          depth--;
350          free_condition(conditions[depth]);
351          conditions[depth] = NULL;
352          cfg->tok = tok_nop;
353          break;
354        case tok_comment:
355        case tok_define:
356        case tok_menuoption:
357        case tok_bool:
358        case tok_tristate:
359        case tok_int:
360        case tok_hex:
361        case tok_choice:
362        case tok_make:
363          /*
364           * We need to duplicate the chain of conditions and attach them to
365           * this token.
366           */
367          cfg->cond = get_token_cond(&conditions[0], depth);
368          break;
369        case tok_dep_tristate:
370          /*
371           * Same as tok_tristate et al except we have a temporary
372           * conditional. (Sort of a hybrid tok_if, tok_tristate, tok_fi
373           * option)
374           */
375          conditions[depth] = cfg->cond;
376          depth++;
377          cfg->cond = get_token_cond(&conditions[0], depth);
378          depth--;
379          free_condition(conditions[depth]);
380          conditions[depth] = NULL;
381        default:
382          break;
383        }
384    }
385
386  /*
387   * Fix any conditions involving the "choice" operator.
388   */
389  fix_choice_cond();
390
391  /*
392   * Walk through and see if there are multiple options that control the
393   * same kvariable.  If there are we need to treat them a little bit
394   * special.
395   */
396  for(cfg=scfg;cfg != NULL; cfg = cfg->next)
397    {
398      switch(cfg->tok)
399        {
400        case tok_bool:
401        case tok_tristate:
402        case tok_dep_tristate:
403        case tok_int:
404        case tok_hex:
405          for(cfg1=cfg;cfg1 != NULL; cfg1 = cfg1->next)
406            {
407              switch(cfg1->tok)
408                {
409                case tok_define:
410                case tok_bool:
411                case tok_tristate:
412                case tok_dep_tristate:
413                case tok_int:
414                case tok_hex:
415                  if( strcmp(cfg->optionname, cfg1->optionname) == 0)
416                    {
417                      cfg->flags |= CFG_DUP;
418                      cfg1->flags |= CFG_DUP;
419                    }
420                  break;
421                default:
422                  break;
423                }
424            }
425          break;
426        default:
427          break;
428        }
429    }
430
431  /*
432   * Now go through the list, and every time we see a kvariable, check
433   * to see whether it also has some dependencies.  If so, then
434   * append it to our list.  The reason we do this is that we might have
435   * option CONFIG_FOO which is only used if CONFIG_BAR is set.  It may
436   * turn out that in config.in that the default value for CONFIG_BAR is
437   * set to "y", but that CONFIG_BAR is not enabled because CONFIG_XYZZY
438   * is not set.  The current condition chain does not reflect this, but
439   * we can fix this by searching for the tokens that this option depends
440   * upon and cloning the conditions and merging them with the list.
441   */
442  for(cfg=scfg;cfg != NULL; cfg = cfg->next)
443    {
444      /*
445       * Search for a token that has a condition list.
446       */
447      if(cfg->cond == NULL) continue;
448      for(cnd = cfg->cond; cnd; cnd=cnd->next)
449        {
450          /*
451           * Now search the condition list for a known configuration variable
452           * that has conditions of its own.
453           */
454          if(cnd->op != op_kvariable) continue;
455          if(cnd->variable.cfg->cond == NULL) continue;
456
457          if(cnd->variable.cfg->flags & CFG_DUP) continue; 
458          /*
459           * OK, we have some conditions to append to cfg.  Make  a clone
460           * of the conditions,
461           */
462          newcond = get_token_cond_frag(cnd->variable.cfg->cond, &last);
463
464          /*
465           * Finally, we splice it into our list.
466           */
467          last->next = cfg->cond;
468          cfg->cond = newcond;
469
470        }
471    }
472
473  /*
474   * There is a strong possibility that we have duplicate conditions
475   * in here.  It would make the script more efficient and readable to
476   * remove these.  Here is where we assume here that there are no
477   * parenthesis in the input script.
478   */
479  for(cfg=scfg;cfg != NULL; cfg = cfg->next)
480    {
481      /*
482       * Search for configuration options that have conditions.
483       */
484      if(cfg->cond == NULL) continue;
485      for(cnd = cfg->cond; cnd; cnd=cnd->next)
486        {
487          /*
488           * Search for a left parenthesis.
489           */
490          if(cnd->op != op_lparen) continue;
491          for(cnd1 = cnd->next; cnd1; cnd1=cnd1->next)
492            {
493              /*
494               * Search after the previous left parenthesis, and try
495               * and find a second left parenthesis.
496               */
497              if(cnd1->op != op_lparen) continue;
498
499              /*
500               * Now compare the next 5 tokens to see if they are
501               * identical.  We are looking for two chains that
502               * are like: '(' $VARIABLE operator constant ')'.
503               */
504              cnd2 = cnd;
505              cnd3 = cnd1;
506              for(i=0; i<5; i++, cnd2=cnd2->next, cnd3=cnd3->next)
507                {
508                  if(!cnd2 || !cnd3) break;
509                  if(cnd2->op != cnd3->op) break;
510                  if(i == 1 && (cnd2->op != op_kvariable
511                     || cnd2->variable.cfg != cnd3->variable.cfg) ) break;
512                  if(i==2 && cnd2->op != op_eq && cnd2->op != op_neq) break;
513                  if(i == 3 && cnd2->op != op_constant &&
514                     strcmp(cnd2->variable.str, cnd3->variable.str) != 0)
515                    break;
516                  if(i==4 && cnd2->op != op_rparen) break;
517                }
518              /*
519               * If these match, and there is an and gluing these together,
520               * then we can nuke the second one.
521               */
522              if(i==5 && ((cnd3 && cnd3->op == op_and)
523                          ||(cnd2 && cnd2->op == op_and)))
524                {
525                  /*
526                   * We have a duplicate.  Nuke 5 ops.
527                   */
528                  cnd3 = cnd1;
529                  for(i=0; i<5; i++, cnd3=cnd3->next)
530                    {
531                      cnd3->op = op_nuked;
532                    }
533                  /*
534                   * Nuke the and that glues the conditions together.
535                   */
536                  if(cnd3 && cnd3->op == op_and) cnd3->op = op_nuked;
537                  else if(cnd2 && cnd2->op == op_and) cnd2->op = op_nuked;
538                }
539            }
540        }
541    }
542}
Note: See TracBrowser for help on using the repository browser.