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

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

Added CeCILL License information

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