source: branches/UKMO/r6232_tracer_advection/NEMOGCM/EXTERNAL/IOIPSL/tools/tkcond.c @ 9295

Last change on this file since 9295 was 9295, checked in by jcastill, 3 years ago

Remove svn keywords

File size: 13.4 KB
Line 
1/* parser config.in
2 * $Id: tkcond.c 2281 2010-10-15 14:21:13Z smasson $
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.