/* jgofs_selection_tester.c */ /* from parse v 2.2 (Apr 09) */ /* Separated test code from parse code to avoid precision issues */ /* Turns out it was better to REALLY avoid the precision issues, and */ /* things could have stayed as one module, but by that time other */ /* changes had been made /* Rename source modules to avoid having a "test.c" */ /* Start new versions at 2.5 for both modules */ /* parse.c */ /* parse and test sections from outer v 3.0a Jun 07 */ /* Separated out so that inners can parse selections. Of interest */ /* for optimization purposes to be sure that vars in selections are */ /* not projected out prematurely. Turns out this premature stuff */ /* would help outer, too, and is much more critical there! (Apr 09) */ #define JG_SEL_TESTER_VERSION "jg_sel_test v 2.5 20 May 2009" /* 20 May 2009. wjs */ /* Check a couple of stack sizes */ /* Do some restructuring */ /* Split tester from parser (see above) */ /* Remove precision-dependent code */ /* [Needs parse.h v 2.2] */ /* [Begin v 2.5] */ /* [Comments below from parse. See jgofs_selection_parser.c for */ /* older comments that might apply to jgofs_selection_tester] */ /* 3 Apr 2009. wjs */ /* Modify jgofs_selection_test to return "Unknown" when it's */ /* unknown if record matches selection criteria. This happens */ /* when criteria are at a lower level than the record being */ /* tested. Previous behavior was to try to return */ /* "accept record" in such cases. The attempt did not succeed */ /* in cases where the "not" logical operator was used */ /* Increase size of stack in anticipation of more parenthesized */ /* stuff from the OOserver subselection page */ /* Change jgofs_selection_test function type to Logical from int */ /* [Needs parse.h v 2.1] */ /* [Begin v 2.2] */ /* P-code form: 128..255 exec test(n-128) [negative #'s on Sun] [negativity not used in this code-fortunately. WJS] 1=or,2=and,3=not 0: end */ #include "parse.h" /* Function in parse_utils */ int strlike(); /* Function in inner */ void iovalstr_(); /* Function in inner (most) or possibly a dummy that calls iovalreal_ */ void iovaldouble_(); /* To allow parse to be used standalone, we don't want inner or */ /* outer functions (like err) to be used by parse. Such functions */ /* are in the parse_functions data structure */ /* We could probably get away w/using the inner/outer versions here */ /* in test, but it's easier to just use the parse_functions structure */ char *jgofs_selection_tester_return_vers() /* Dummy routine. Exists only to force .h file version string into */ /* this module. Note string must not be global or we'll have con- */ /* flicts if another routine similarly includes the version string */ { static char version[] = JG_SEL_TESTER_VERSION"/"FULL_PARSEH_VERSION; return version; } Logical jgofs_selection_tester(cl,parse_data_struct,parse_function_struct) int cl; struct parse_data *parse_data_struct; struct parse_functions *parse_function_struct; { double f; static char tmp[DATUMSIZE+1],uptmp[DATUMSIZE+1]; int i,j,m; char *datum_ptr,*test_condition_ptr; /* Size below comes from conclusion that in tstproccnt loop below, */ /* the stack pointer could be incremented once each interaction, */ /* even though logically, I think the pointer is supposed to end up 0 */ Logical stk[N_JG_SEL_OPERATIONS]; int stkp; /* Stuff from parse interface structures */ /* Data */ int tstcnt; int tstproccnt; struct parse_test_struct *tst; int *tstproc; tstcnt = parse_data_struct->tstcnt; tstproccnt = parse_data_struct->tstproccnt; tst = parse_data_struct->tst; tstproc = parse_data_struct->tstproc; #ifdef DEBUG printf("entered test at level %d\n",cl); #endif stkp = -1; stk[0] = TRUE; for (i=0; i= 128) { j = tstproc[i]-128; /* We have a relational test to perform, whose result will be */ /* put on the stack. The result depends on whether we "have the */ /* data available"; ie, the level we're at. No matter what, */ /* though, we will add to the stack, so check that up top */ if (++stkp == N_JG_SEL_OPERATIONS) parse_function_struct->errn ("Internal error: Temp stack overflow. Max =",N_JG_SEL_OPERATIONS); /* Use previously saved test result-no need to recalc it */ if (tst[j].testlev < cl) stk[stkp] = tst[j].testres; /* Test is at level we haven't gotten to yet. Return true even */ /* if data at next level might not pass test. Therefore, all */ /* level N data shows up when displaying level N, but might not */ /* when displaying same object at level N+1. Only a problem */ /* when displaying by level... */ /* Too bad that putting TRUE on the stack doesn't mean */ /* that TRUE gets returned... esp if next thing on stack is NOT! */ if (tst[j].testlev > cl) stk[stkp] = NOT_VALID; /* We are at the level where the test needs to happen - do it */ if (tst[j].testlev == cl) { if (tst[j].testcode < 0) { iovalstr_(&(tst[j].testvar),tmp); if (tst[j].case_sensitive) { datum_ptr = tmp; test_condition_ptr = tst[j].teststr; } else { m = 0; while (tmp[m] != '\0') { uptmp[m] = toupper(tmp[m]); m++; } uptmp[m] = '\0'; datum_ptr = uptmp; test_condition_ptr = tst[j].upteststr; } if (tst[j].testcode >= -6) f = (double) strcmp(datum_ptr,test_condition_ptr); } else { iovaldouble_(&(tst[j].testvar),&f); } /* Suspect that abs in next line not needed. Comments in */ /* parse spoke of ">128 neg #s on SUN", followed by later */ /* comment that this feature not used */ switch (abs(tst[j].testcode)) { case 1: m = (f < tst[j].testval); break; case 2: m = (f == tst[j].testval); break; case 3: m = (f > tst[j].testval); break; case 4: m = (f <= tst[j].testval); break; case 5: m = (f != tst[j].testval); break; case 6: m = (f >= tst[j].testval); break; case 7: /* contains */ m = (strstr(datum_ptr,test_condition_ptr) != NULL); break; case 8: /* is_contained_in */ m = (strstr(test_condition_ptr,datum_ptr) != NULL); break; case 9: /* begins_with */ m = (strncmp(datum_ptr,test_condition_ptr,tst[j].lenteststr) == 0); break; case 10: /* ends_with */ m = strlen(datum_ptr) - tst[j].lenteststr; m = (m >= 0) ? (strcmp(datum_ptr+m,test_condition_ptr) == 0) : FALSE; break; case 11: /* like */ m = (strlike(datum_ptr, test_condition_ptr, parse_function_struct->err) == 0); break; default: parse_function_struct->errn ("Internal error-illegal testcode",tst[j].testcode); } tst[j].testres = m; stk[stkp] = m; } } else { switch (tstproc[i]) { /* Note that we are in a ternary logic situation here. Value */ /* on stack can be TRUE, FALSE, or NOT_VALID */ case 1: /* OR operator */ if ((stk[stkp-1] == TRUE) || (stk[stkp] == TRUE)) { stk[stkp-1] = TRUE; } else if ((stk[stkp-1] == FALSE) && (stk[stkp] == FALSE)) { stk[stkp-1] = FALSE; } else { stk[stkp-1] = NOT_VALID; } stkp--; break; case 2: /* AND operator */ if ((stk[stkp-1] == FALSE) || (stk[stkp] == FALSE)) { stk[stkp-1] = FALSE; } else if ((stk[stkp-1] == TRUE) && (stk[stkp] == TRUE)) { stk[stkp-1] = TRUE; } else { stk[stkp-1] = NOT_VALID; } stkp--; break; case 3: /* NOT operator */ /* If value on stack is NOT_VALID, negating it also results */ /* in NOT_VALID, so we don't need to change anything on the */ /* stack */ if (stk[stkp] != NOT_VALID) stk[stkp] = ( ! stk[stkp]); break; default: parse_function_struct->errn ("Internal error-illegal tstproc",tstproc[i]); } } } #ifdef DEBUG printf("test result = %d\n",stk[0]); #endif /* Don't know if test changes the next 2 or if these arrays */ /* are even meaningful after a run through test - too lazy to check */ parse_data_struct->tstcnt = tstcnt; parse_data_struct->tstproccnt = tstproccnt; if ((tstproccnt != 0) && (stkp != 0)) parse_function_struct->errn ("Internal error-selection result stack has >1 entry. Size",stkp+1); return stk[0]; }