Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

pdl_rule.c

Go to the documentation of this file.
00001 /*
00002  *   Copyright (c) 2003 EU DataGrid        http://www.eu-datagrid.org/
00003  *
00004  *   $Id: pdl_rule.c,v 1.19 2003/09/04 13:47:35 martijn Exp $
00005  *
00006  *   Copyright (c) 2003 by
00007  *      G.M. Venekamp <venekamp@nikhef.nl>
00008  *      NIKHEF Amsterdam, the Netherlands
00009  *
00010  *   This software is distributed under a BSD-style open source
00011  *   licence. For a complete description of the licence take a look
00012  *   at: http://eu-datagrid.web.cern.ch/eu-datagrid/license.html
00013  *
00014  */
00015 
00016 
00030 #include <stdarg.h>
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 
00035 #include "lcmaps_log.h"
00036 #include "pdl_rule.h"
00037 #include "pdl_policy.h"
00038 #include "pdl_variable.h"
00039 
00040 const rule_t *top_rule=0;
00041 rule_t *last_rule=0;
00042 static BOOL add_new_rules = TRUE;
00043 
00044 rule_t* _add_rule(const record_t* state, const record_t* true_branch, const record_t* false_branch);
00045 const rule_t* find_state(const rule_t* rule, const char* state);
00046 recursion_t has_recursion(const rule_t* rule, int* list, int unsigned depth, unsigned int* seen_rules);
00047 int find_insert_position(const int* list, const int rule_number, unsigned int high);
00048 unsigned int rule_number(const rule_t* rule);
00049 BOOL make_list(int* new_list, const int* list, const int rule_number, const unsigned int depth);
00050 unsigned int count_rules(const rule_t* rule);
00051 void update_list(unsigned int* rules, unsigned int rule);
00052 const rule_t* get_rule_number(unsigned int rule_num);
00053 
00054 
00058 void start_new_rules(void)
00059 {
00060   //  lcmaps_log_debug(1, "start_new_rule: starting new rules.\n");
00061   top_rule = last_rule = 0;
00062 }
00063 
00064 
00071 void allow_new_rules(BOOL allow)
00072 {
00073   add_new_rules = allow;
00074 }
00075 
00076 
00086 rule_t* add_rule(record_t* state, record_t* true_branch,
00087                  record_t* false_branch)
00088 {
00089   /*
00090    *  This is for a somewhat dirty hack.
00091    *
00092    *  The yacc parser cannot determine the end of expression of the 
00093    *  form 'a -> b'. This is because 'a -> b' might be followed
00094    *  by '| c'. Therefore, yacc continues to scan on the next line.
00095    *  Lex on the other hand simply looks at new lines and increases
00096    *  the line counter each time it sees one. When an error needs
00097    *  to be reported, the global line number might be advanced too
00098    *  far. To counter act this problem, the state variable contains
00099    *  the right line number, i.e. the line where the current rule
00100    *  was started. By setting the the global lineno variable to the
00101    *  correct line number; doing out stuff; and finally setting back
00102    *  the lineno variable to its original value, we circumvent the
00103    *  problem of misreported line nubmers.
00104    */
00105   rule_t* rule = 0;
00106 
00107   if (!(rule = _add_rule(state, true_branch, false_branch))) {
00108     free(state->string);
00109     if (true_branch)  free(true_branch->string);
00110     if (false_branch) free(false_branch->string);
00111   }
00112 
00113   free(state);
00114   if (true_branch)  free(true_branch);
00115   if (false_branch) free(false_branch);
00116 
00117   return rule;
00118 }
00119 
00120 
00144 rule_t* _add_rule(const record_t* state, const record_t* true_branch,
00145                   const record_t* false_branch)
00146 {
00147   rule_t* rule;
00148   policy_t* policy;
00149 
00150   if ((policy = find_policy(state->string))) {
00151     warning(PDL_ERROR, "Left hand side of a rule cannot be a policy; see also line %d.", policy->lineno);
00152     return 0;
00153   }
00154 
00155   //  cast away constness.
00156   if ((rule = (rule_t*)find_state(top_rule, state->string))) {
00157     warning(PDL_ERROR, "State '%s' is already in use. See line %d.\n", state->string, rule->lineno);
00158     return 0;
00159   }
00160 
00161   if ((true_branch && (policy = find_policy(true_branch->string)))
00162       || (false_branch && (policy = find_policy(false_branch->string))))
00163     warning(PDL_ERROR, "Rule contians reference to a policy. This is currently not supported.");
00164   /*  Do not return 0 if a warning has been issued. Ignore the fact that  *
00165    *  a policy rule has been used. Use its name as a module instead.      */
00166 
00167   //  Check if we just needed to check the rule for errors, or
00168   //  add the rule after checking as well.
00169   if (!add_new_rules)
00170     return 0;
00171 
00172   if (!(rule = (rule_t *)malloc(sizeof(rule_t)))) {
00173     warning(PDL_ERROR, "out of memory.");
00174     return 0;
00175   }
00176 
00177   rule->state        = state->string;
00178   rule->true_branch  = true_branch  ? true_branch->string  : 0;
00179   rule->false_branch = false_branch ? false_branch->string : 0;
00180   rule->lineno       = state->lineno;
00181   rule->next         = 0;
00182 
00183   if (top_rule)
00184     last_rule->next = rule;
00185   else
00186     top_rule = rule;
00187   
00188   last_rule = rule;
00189 
00190   return rule;
00191 }
00192 
00193 
00202 const rule_t* find_state(const rule_t* rule, const char* state)
00203 {
00204   if (!rule || !state)
00205     return 0;
00206 
00207   while (rule && strcmp(state, rule->state))
00208     rule = rule->next;
00209 
00210   return rule;
00211 }
00212 
00213 
00220 BOOL check_rule_for_recursion(const rule_t* rule)
00221 {
00222   unsigned int num_rules = count_rules(rule);
00223   unsigned int *rules;
00224   recursion_t rc;
00225   int i;
00226 
00227   rules = (unsigned int*)calloc((num_rules+1), sizeof(*rules));
00228 
00229   top_rule = rule;
00230 
00231   rc = has_recursion(rule, 0, 0, rules);
00232 
00233   if (rules[0] != num_rules) {
00234     int j=1;
00235     for (i=1; i<=num_rules; i++) {
00236       if (rules[j] != i) {
00237         lineno = get_rule_number(i-1)->lineno;
00238         warning(PDL_WARNING, "rule is not part of the chain.");
00239       } else
00240         j++;
00241     }
00242   }
00243 
00244   free(rules);
00245 
00246   return (rc & RECURSION) ? TRUE : FALSE;
00247 }
00248 
00249 
00257 unsigned int count_rules(const rule_t* rule)
00258 {
00259   unsigned int c=0;
00260 
00261   while (rule) {
00262     c++;
00263     rule = rule->next;
00264   }
00265 
00266   return c;
00267 }
00268 
00269 
00278 const rule_t* get_rule_number(unsigned int rule_num)
00279 {
00280   unsigned int i;
00281   const rule_t* r;
00282 
00283   for (i=0, r=top_rule; r && i<rule_num; i++)
00284     r = r->next;
00285 
00286   return r;
00287 }
00288 
00289 
00318 recursion_t has_recursion(const rule_t* rule, int* list, unsigned int depth,
00319                           unsigned int* seen_rules)
00320 {
00321   unsigned int rule_num;
00322   recursion_t rc;
00323   int* new_list = 0;
00324 
00325   ++depth;
00326 
00327   /*
00328    *  If there is no more rule we have reached the end and hance did
00329    *  no encounter any recursion.
00330    */
00331   if (!rule)
00332     return NO_RECURSION;
00333 
00334   /*
00335    *  Allocate memory to hold a new list. The number of elements in
00336    *  the equals the depth of the tree.
00337    */
00338   new_list = (int*)malloc(depth*sizeof(int));
00339   rc       = NO_RECURSION;
00340   rule_num = rule_number(rule);
00341 
00342   //  Update the list of visited rules.
00343   update_list(seen_rules, rule_num);
00344 
00345   /*
00346    *  First make a new list, this list is based upon the current list.
00347    *  The list is extended with the current state. If the list cannot
00348    *  be created, because the current state is already in the list, we
00349    *  have found recursion and the current path is abandoned.
00350    */
00351   if (make_list(new_list, list, rule_num, depth)) {
00352     //  No recursion yet, keep on looking...
00353     recursion_t rcl=rc, rcr=rc;
00354 
00355     //  If present, check if the true branch has any recursion.
00356     if (rule->true_branch) {
00357       rcl = has_recursion(find_state(top_rule, rule->true_branch), new_list, depth, seen_rules);
00358 
00359       /*
00360        *  Check the return condition. It tells whether recursion has
00361        *  been found. If so, it also tells if the recursion has been
00362        *  reported. This is to prevent multiple error messages on the
00363        *  same rule for the same path.
00364        *
00365        *  NOTE: When the same rule causes a recursion in a different
00366        *        path, the error is displayed for each of these paths.
00367        *
00368        */
00369       if ((rcl&RECURSION) && !(rcl&RECURSION_HANDLED)) {
00370         lineno = rule->lineno;
00371         if (rule->false_branch)
00372           warning(PDL_ERROR, "rule  %s -> %s | %s causes infinite loop on true transition %s.", rule->state, rule->true_branch, rule->false_branch, rule->true_branch);
00373         else
00374           warning(PDL_ERROR, "rule  %s -> %s causes infinite loop on transition %s.", rule->state, rule->true_branch, rule->true_branch);
00375 
00376         /*
00377          *  Set the handled bit to indicate that the current recursion
00378          *  has been reported.
00379          */
00380         rcl |= RECURSION_HANDLED;
00381       }
00382     }
00383     
00384     //  If present, check if the false branch has any recursion.
00385     if (rule->false_branch) {
00386       rcr = has_recursion(find_state(top_rule, rule->false_branch), new_list, depth, seen_rules);
00387 
00388       if ((rcr&RECURSION) && !(rcr&RECURSION_HANDLED)) {
00389         lineno = rule->lineno;
00390         if (rule->true_branch)
00391           warning(PDL_ERROR, "rule  %s -> %s | %s causes infinite loop on false transition %s.", rule->state, rule->true_branch, rule->false_branch, rule->false_branch);
00392         else
00393           warning(PDL_ERROR, "rule ~%s -> %s causes infinite loop on transition %s.", rule->state, rule->false_branch, rule->false_branch);
00394 
00395         rcr |= RECURSION_HANDLED;
00396       }
00397     }
00398 
00399     /*
00400      *  Join the results of both the true branch and false branch
00401      *  together. This is the result for the current node in the tree.
00402      */
00403     rc = rcl|rcr;
00404   } else
00405     //  Well, there it is, recursion!
00406     rc = RECURSION;
00407 
00408   /*
00409    *  Let's be nice and free the allocated memory to hold the traveled
00410    *  path.
00411    */
00412   free(new_list);
00413 
00414   return rc;
00415 }
00416 
00417 
00425 unsigned int rule_number(const rule_t* rule)
00426 {
00427   int n;
00428   const rule_t* t;
00429 
00430   for (n=0, t=top_rule; t; ++n, t=t->next) {
00431     if (t==rule) break;
00432   }
00433 
00434   return n;
00435 }
00436 
00437 
00453 void update_list(unsigned int* rules, unsigned int rule)
00454 {
00455   //  Rules in the list start at 1.
00456   int p = 1 + find_insert_position(rules+1, rule, rules[0]);
00457 
00458   //  Same here, rules in the list start at 1.
00459   rule++;
00460 
00461   //  Find if the rule number needs to be inserted.
00462   if (rules[p] != rule) {
00463     if (p <= rules[0])
00464       memmove(rules+p+1, rules+p, (1+rules[0]-p)*sizeof(unsigned int));
00465     rules[p] = rule;
00466 
00467     /*
00468      *  Do not forget to reflect the fact that a new rule has been
00469      *  added to the list.
00470      */
00471     rules[0]++;
00472   }
00473 }
00474 
00475 
00488 int find_insert_position(const int* list, const int rule_number, unsigned int high)
00489 {
00490   int low=0, mid;
00491 
00492   /*
00493    *  low and high specify the current part of the list to be
00494    *  examined. Once the search space has become zero, the insertion
00495    *  position has been found.
00496    */
00497   while (low<high) {
00498     mid = (low+high)/2;           //  Determine the middle of the list.
00499 
00500     //  Determine on which side of the middle the insertion point lays.
00501     if (rule_number < list[mid])
00502       high = mid;
00503     else
00504       low = mid + 1;
00505   }
00506 
00507   //  High points to the correct position now.
00508   return high;
00509 }
00510 
00511 
00526 BOOL make_list(int* new_list, const int* list, const int rule_number,
00527                const unsigned int depth)
00528 {
00529   int insert;
00530 
00531   if (!list) {
00532     *new_list = rule_number;
00533     return TRUE;
00534   }
00535 
00536   insert = find_insert_position(list, rule_number, depth-1);
00537 
00538   if ((insert>0) && (list[insert-1] == rule_number))
00539     return FALSE;
00540 
00541   memcpy(new_list, list, insert*sizeof(int));
00542   if ((depth-insert-1)>0)
00543     memcpy(new_list+insert+1, list+insert, (depth-insert-1)*sizeof(int));
00544 
00545   new_list[insert] = rule_number;
00546 
00547   return TRUE;
00548 }
00549 
00550 
00558 void reduce_rule(rule_t* rule)
00559 {
00560   /*
00561    *  The state part of the rule can be a variable. It cannot be
00562    *  another policy rule. Therefore, it can be either reduced
00563    *  to a variable, or the state part is as it is.
00564    */
00565   reduce_to_var(&rule->state, STATE);
00566 
00567   /*
00568    *  In case of the true branch of a rule, it can be one of three
00569    *  things:
00570    *    1 - a variable;
00571    *    2 - a policy rule;
00572    *    3 - a state.
00573    *  Therefore, the value of the true branch is first reduced to
00574    *  a variable. If not successful, it is reduced to a policy rule;
00575    *  which is reduced to a set of rules. If it is not a variable or
00576    *  policy rule, the value is taken as it is.
00577    */
00578   reduce_to_var(&rule->true_branch, TRUE_BRANCH);
00579 
00580   //  See comment on the previous statements.
00581   reduce_to_var(&rule->false_branch, FALSE_BRANCH);
00582 }
00583 
00584 
00585 const rule_t* get_rule(const char* rule, side_t side)
00586 {
00587   const rule_t* r = top_rule;
00588 
00589   switch (side) {
00590   case left_side:
00591     while (r && (strcmp(r->state, rule)!=0)) {
00592       r = r->next;
00593     }
00594     break;
00595   case right_side:
00596     while (r && ((r->true_branch && (strcmp(r->true_branch, rule)!=0)) ||
00597                 ((r->false_branch && strcmp(r->false_branch, rule)!=0)))) {
00598       r = r->next;
00599     }
00600     break;
00601   default:
00602     r = 0;
00603   }
00604 
00605   return r;
00606 }
00607 
00608 
00615 void show_rules(const rule_t* rule)
00616 {
00617   while (rule) {
00618     if (rule->true_branch) {
00619       if (!rule->false_branch) 
00620         lcmaps_log_debug(1, " %s -> %s\n", rule->state, rule->true_branch);
00621       else
00622         lcmaps_log_debug(1, " %s -> %s | %s\n", rule->state, rule->true_branch, rule->false_branch);
00623     } else
00624       lcmaps_log_debug(1, "~%s -> %s\n", rule->state, rule->false_branch);
00625 
00626     rule = rule->next;
00627   }
00628 }
00629 
00630 
00637 void free_rules(rule_t* rule)
00638 {
00639   rule_t* tmp;
00640 
00641   while (rule) {
00642     tmp = rule->next;
00643     free((char *)rule->state);
00644     free((char *)rule->true_branch);
00645     free((char *)rule->false_branch);
00646     free((char *)rule);
00647     rule = tmp;
00648   }
00649 }
00650 
00651 
00658 const rule_t* get_top_rule(void)
00659 {
00660   return top_rule;
00661 }
00662 
00663 
00670 void set_top_rule(const rule_t* rule)
00671 {
00672   top_rule = rule;
00673 }

Generated at Thu Mar 4 17:39:03 2004 for edg-lcmaps by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001