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) Members of the EGEE Collaboration. 2004.
00003  * See http://eu-egee.org/partners/ for details on the copyright holders.
00004  * For license conditions see the license file or
00005  * http://eu-egee.org/license.html
00006  */
00007 
00008 /*
00009  *   Copyright (c) 2003 EU DataGrid        http://www.eu-datagrid.org/
00010  *
00011  *   $Id: pdl_rule.c,v 1.22 2005/03/01 17:05:10 venekamp Exp $
00012  *
00013  *   Copyright (c) 2003 by
00014  *      G.M. Venekamp <venekamp@nikhef.nl>
00015  *      NIKHEF Amsterdam, the Netherlands
00016  *
00017  *   This software is distributed under a BSD-style open source
00018  *   licence. For a complete description of the licence take a look
00019  *   at: http://eu-datagrid.web.cern.ch/eu-datagrid/license.html
00020  *
00021  */
00022 
00023 
00037 #include <stdarg.h>
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 
00042 #include "lcmaps_log.h"
00043 #include "pdl_rule.h"
00044 #include "pdl_policy.h"
00045 #include "pdl_variable.h"
00046 
00047 const rule_t *top_rule=0;
00048 rule_t *last_rule=0;
00049 static BOOL add_new_rules = TRUE;
00050 
00051 rule_t* _add_rule(const record_t* state, const record_t* true_branch, const record_t* false_branch);
00052 const rule_t* find_state(const rule_t* rule, const char* state);
00053 recursion_t has_recursion(const rule_t* rule, int* list, int unsigned depth, unsigned int* seen_rules);
00054 int find_insert_position(const int* list, const int rule_number, unsigned int high);
00055 unsigned int rule_number(const rule_t* rule);
00056 BOOL make_list(int* new_list, const int* list, const int rule_number, const unsigned int depth);
00057 unsigned int count_rules(const rule_t* rule);
00058 void update_list(unsigned int* rules, unsigned int rule);
00059 const rule_t* get_rule_number(unsigned int rule_num);
00060 
00061 
00065 void start_new_rules(void)
00066 {
00067   //  lcmaps_log_debug(1, "start_new_rule: starting new rules.\n");
00068   top_rule = last_rule = 0;
00069 }
00070 
00071 
00078 void allow_new_rules(BOOL allow)
00079 {
00080   add_new_rules = allow;
00081 }
00082 
00083 
00093 rule_t* add_rule(record_t* state, record_t* true_branch,
00094                  record_t* false_branch)
00095 {
00096   /*
00097    *  This is for a somewhat dirty hack.
00098    *
00099    *  The yacc parser cannot determine the end of expression of the 
00100    *  form 'a -> b'. This is because 'a -> b' might be followed
00101    *  by '| c'. Therefore, yacc continues to scan on the next line.
00102    *  Lex on the other hand simply looks at new lines and increases
00103    *  the line counter each time it sees one. When an error needs
00104    *  to be reported, the global line number might be advanced too
00105    *  far. To counter act this problem, the state variable contains
00106    *  the right line number, i.e. the line where the current rule
00107    *  was started. By setting the the global lineno variable to the
00108    *  correct line number; doing out stuff; and finally setting back
00109    *  the lineno variable to its original value, we circumvent the
00110    *  problem of misreported line nubmers.
00111    */
00112   rule_t* rule = 0;
00113 
00114   if (!(rule = _add_rule(state, true_branch, false_branch))) {
00115     free(state->string);
00116     if (true_branch)  free(true_branch->string);
00117     if (false_branch) free(false_branch->string);
00118   }
00119 
00120   free(state);
00121   if (true_branch)  free(true_branch);
00122   if (false_branch) free(false_branch);
00123 
00124   return rule;
00125 }
00126 
00127 
00151 rule_t* _add_rule(const record_t* state, const record_t* true_branch,
00152                   const record_t* false_branch)
00153 {
00154   rule_t* rule;
00155   policy_t* policy;
00156 
00157   if ((policy = find_policy(state->string))) {
00158     warning(PDL_ERROR, "Left hand side of a rule cannot be a policy; see also line %d.", policy->lineno);
00159     return 0;
00160   }
00161 
00162   //  cast away constness.
00163   if ((rule = (rule_t*)find_state(top_rule, state->string))) {
00164     warning(PDL_ERROR, "State '%s' is already in use. See line %d.\n", state->string, rule->lineno);
00165     return 0;
00166   }
00167 
00168   if ((true_branch && (policy = find_policy(true_branch->string)))
00169       || (false_branch && (policy = find_policy(false_branch->string))))
00170     warning(PDL_ERROR, "Rule contians reference to a policy. This is currently not supported.");
00171   /*  Do not return 0 if a warning has been issued. Ignore the fact that  *
00172    *  a policy rule has been used. Use its name as a module instead.      */
00173 
00174   //  Check if we just needed to check the rule for errors, or
00175   //  add the rule after checking as well.
00176   if (!add_new_rules)
00177     return 0;
00178 
00179   if (!(rule = (rule_t *)malloc(sizeof(rule_t)))) {
00180     warning(PDL_ERROR, "out of memory.");
00181     return 0;
00182   }
00183 
00184   rule->state        = state->string;
00185   rule->true_branch  = true_branch  ? true_branch->string  : 0;
00186   rule->false_branch = false_branch ? false_branch->string : 0;
00187   rule->lineno       = state->lineno;
00188   rule->next         = 0;
00189 
00190   if (top_rule)
00191     last_rule->next = rule;
00192   else
00193     top_rule = rule;
00194   
00195   last_rule = rule;
00196 
00197   return rule;
00198 }
00199 
00200 
00210 const rule_t* find_state(const rule_t* rule, const char* state)
00211 {
00212   if (!rule || !state)
00213     return 0;
00214 
00215   while (rule && strcmp(state, rule->state))
00216     rule = rule->next;
00217 
00218   return rule;
00219 }
00220 
00221 
00228 BOOL check_rule_for_recursion(const rule_t* rule)
00229 {
00230   unsigned int num_rules = count_rules(rule);
00231   unsigned int *rules;
00232   recursion_t rc;
00233   int i;
00234 
00235   rules = (unsigned int*)calloc((num_rules+1), sizeof(*rules));
00236 
00237   top_rule = rule;
00238 
00239   rc = has_recursion(rule, 0, 0, rules);
00240 
00241   if (rules[0] != num_rules) {
00242     int j=1;
00243     for (i=1; i<=num_rules; i++) {
00244       if (rules[j] != i) {
00245         lineno = get_rule_number(i-1)->lineno;
00246         warning(PDL_WARNING, "rule is not part of the chain.");
00247       } else
00248         j++;
00249     }
00250   }
00251 
00252   free(rules);
00253 
00254   return (rc & RECURSION) ? TRUE : FALSE;
00255 }
00256 
00257 
00265 unsigned int count_rules(const rule_t* rule)
00266 {
00267   unsigned int c=0;
00268 
00269   while (rule) {
00270     c++;
00271     rule = rule->next;
00272   }
00273 
00274   return c;
00275 }
00276 
00277 
00286 const rule_t* get_rule_number(unsigned int rule_num)
00287 {
00288   unsigned int i;
00289   const rule_t* r;
00290 
00291   for (i=0, r=top_rule; r && i<rule_num; i++)
00292     r = r->next;
00293 
00294   return r;
00295 }
00296 
00297 
00326 recursion_t has_recursion(const rule_t* rule, int* list, unsigned int depth,
00327                           unsigned int* seen_rules)
00328 {
00329   unsigned int rule_num;
00330   recursion_t rc;
00331   int* new_list = 0;
00332 
00333   ++depth;
00334 
00335   /*
00336    *  If there is no more rule we have reached the end and hance did
00337    *  no encounter any recursion.
00338    */
00339   if (!rule)
00340     return NO_RECURSION;
00341 
00342   /*
00343    *  Allocate memory to hold a new list. The number of elements in
00344    *  the equals the depth of the tree.
00345    */
00346   new_list = (int*)malloc(depth*sizeof(int));
00347   rc       = NO_RECURSION;
00348   rule_num = rule_number(rule);
00349 
00350   //  Update the list of visited rules.
00351   update_list(seen_rules, rule_num);
00352 
00353   /*
00354    *  First make a new list, this list is based upon the current list.
00355    *  The list is extended with the current state. If the list cannot
00356    *  be created, because the current state is already in the list, we
00357    *  have found recursion and the current path is abandoned.
00358    */
00359   if (make_list(new_list, list, rule_num, depth)) {
00360     //  No recursion yet, keep on looking...
00361     recursion_t rcl=rc, rcr=rc;
00362 
00363     //  If present, check if the true branch has any recursion.
00364     if (rule->true_branch) {
00365       rcl = has_recursion(find_state(top_rule, rule->true_branch), new_list, depth, seen_rules);
00366 
00367       /*
00368        *  Check the return condition. It tells whether recursion has
00369        *  been found. If so, it also tells if the recursion has been
00370        *  reported. This is to prevent multiple error messages on the
00371        *  same rule for the same path.
00372        *
00373        *  NOTE: When the same rule causes a recursion in a different
00374        *        path, the error is displayed for each of these paths.
00375        *
00376        */
00377       if ((rcl&RECURSION) && !(rcl&RECURSION_HANDLED)) {
00378   lineno = rule->lineno;
00379   if (rule->false_branch)
00380     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);
00381   else
00382     warning(PDL_ERROR, "rule  %s -> %s causes infinite loop on transition %s.", rule->state, rule->true_branch, rule->true_branch);
00383 
00384   /*
00385    *  Set the handled bit to indicate that the current recursion
00386    *  has been reported.
00387    */
00388   rcl |= RECURSION_HANDLED;
00389       }
00390     }
00391     
00392     //  If present, check if the false branch has any recursion.
00393     if (rule->false_branch) {
00394       rcr = has_recursion(find_state(top_rule, rule->false_branch), new_list, depth, seen_rules);
00395 
00396       if ((rcr&RECURSION) && !(rcr&RECURSION_HANDLED)) {
00397   lineno = rule->lineno;
00398   if (rule->true_branch)
00399     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);
00400   else
00401     warning(PDL_ERROR, "rule ~%s -> %s causes infinite loop on transition %s.", rule->state, rule->false_branch, rule->false_branch);
00402 
00403   rcr |= RECURSION_HANDLED;
00404       }
00405     }
00406 
00407     /*
00408      *  Join the results of both the true branch and false branch
00409      *  together. This is the result for the current node in the tree.
00410      */
00411     rc = rcl|rcr;
00412   } else
00413     //  Well, there it is, recursion!
00414     rc = RECURSION;
00415 
00416   /*
00417    *  Let's be nice and free the allocated memory to hold the traveled
00418    *  path.
00419    */
00420   free(new_list);
00421 
00422   return rc;
00423 }
00424 
00425 
00433 unsigned int rule_number(const rule_t* rule)
00434 {
00435   int n;
00436   const rule_t* t;
00437 
00438   for (n=0, t=top_rule; t; ++n, t=t->next) {
00439     if (t==rule) break;
00440   }
00441 
00442   return n;
00443 }
00444 
00445 
00461 void update_list(unsigned int* rules, unsigned int rule)
00462 {
00463   //  Rules in the list start at 1.
00464   int p = 1 + find_insert_position(rules+1, rule, rules[0]);
00465 
00466   //  Same here, rules in the list start at 1.
00467   rule++;
00468 
00469   //  Find if the rule number needs to be inserted.
00470   if (rules[p] != rule) {
00471     if (p <= rules[0])
00472       memmove(rules+p+1, rules+p, (1+rules[0]-p)*sizeof(unsigned int));
00473     rules[p] = rule;
00474 
00475     /*
00476      *  Do not forget to reflect the fact that a new rule has been
00477      *  added to the list.
00478      */
00479     rules[0]++;
00480   }
00481 }
00482 
00483 
00496 int find_insert_position(const int* list, const int rule_number, unsigned int high)
00497 {
00498   int low=0, mid;
00499 
00500   /*
00501    *  low and high specify the current part of the list to be
00502    *  examined. Once the search space has become zero, the insertion
00503    *  position has been found.
00504    */
00505   while (low<high) {
00506     mid = (low+high)/2;           //  Determine the middle of the list.
00507 
00508     //  Determine on which side of the middle the insertion point lays.
00509     if (rule_number < list[mid])
00510       high = mid;
00511     else
00512       low = mid + 1;
00513   }
00514 
00515   //  High points to the correct position now.
00516   return high;
00517 }
00518 
00519 
00534 BOOL make_list(int* new_list, const int* list, const int rule_number,
00535                const unsigned int depth)
00536 {
00537   int insert;
00538 
00539   if (!list) {
00540     *new_list = rule_number;
00541     return TRUE;
00542   }
00543 
00544   insert = find_insert_position(list, rule_number, depth-1);
00545 
00546   if ((insert>0) && (list[insert-1] == rule_number))
00547     return FALSE;
00548 
00549   memcpy(new_list, list, insert*sizeof(int));
00550   if ((depth-insert-1)>0)
00551     memcpy(new_list+insert+1, list+insert, (depth-insert-1)*sizeof(int));
00552 
00553   new_list[insert] = rule_number;
00554 
00555   return TRUE;
00556 }
00557 
00558 
00566 void reduce_rule(rule_t* rule)
00567 {
00568   /*
00569    *  The state part of the rule can be a variable. It cannot be
00570    *  another policy rule. Therefore, it can be either reduced
00571    *  to a variable, or the state part is as it is.
00572    */
00573   reduce_to_var(&rule->state, STATE);
00574 
00575   /*
00576    *  In case of the true branch of a rule, it can be one of three
00577    *  things:
00578    *    1 - a variable;
00579    *    2 - a policy rule;
00580    *    3 - a state.
00581    *  Therefore, the value of the true branch is first reduced to
00582    *  a variable. If not successful, it is reduced to a policy rule;
00583    *  which is reduced to a set of rules. If it is not a variable or
00584    *  policy rule, the value is taken as it is.
00585    */
00586   reduce_to_var(&rule->true_branch, TRUE_BRANCH);
00587 
00588   //  See comment on the previous statements.
00589   reduce_to_var(&rule->false_branch, FALSE_BRANCH);
00590 }
00591 
00592 
00593 const rule_t* get_rule(const char* rule, side_t side)
00594 {
00595   const rule_t* r = top_rule;
00596 
00597   switch (side) {
00598   case left_side:
00599     while (r && (strcmp(r->state, rule)!=0)) {
00600       r = r->next;
00601     }
00602     break;
00603   case right_side:
00604     while (r && ((r->true_branch && (strcmp(r->true_branch, rule)!=0)) ||
00605           ((r->false_branch && strcmp(r->false_branch, rule)!=0)))) {
00606       r = r->next;
00607     }
00608     break;
00609   default:
00610     r = 0;
00611   }
00612 
00613   return r;
00614 }
00615 
00616 
00623 void show_rules(const rule_t* rule)
00624 {
00625   while (rule) {
00626     if (rule->true_branch) {
00627       if (!rule->false_branch) 
00628   lcmaps_log_debug(1, " %s -> %s\n", rule->state, rule->true_branch);
00629       else
00630   lcmaps_log_debug(1, " %s -> %s | %s\n", rule->state, rule->true_branch, rule->false_branch);
00631     } else
00632       lcmaps_log_debug(1, "~%s -> %s\n", rule->state, rule->false_branch);
00633 
00634     rule = rule->next;
00635   }
00636 }
00637 
00638 
00645 void free_rules(rule_t* rule)
00646 {
00647   rule_t* tmp;
00648 
00649   while (rule) {
00650     tmp = rule->next;
00651     free((char *)rule->state);        rule->state        = NULL;  //  Does not hurt to clear it...
00652     free((char *)rule->true_branch);  rule->true_branch  = NULL;  //  Does not hurt to clear it...
00653     free((char *)rule->false_branch); rule->false_branch = NULL;  //  Does not hurt to clear it...
00654     free((char *)rule);
00655     rule = tmp;
00656   }
00657 }
00658 
00659 
00666 const rule_t* get_top_rule(void)
00667 {
00668   return top_rule;
00669 }
00670 
00671 
00678 void set_top_rule(const rule_t* rule)
00679 {
00680   top_rule = rule;
00681 }

Generated on Sun May 29 21:22:11 2005 for lcmaps by doxygen 1.3.5