/* e4parse.c (c)Copyright Sequiter Software Inc., 1988-1996. All rights reserved. */ /* Restrictions - STR can only have a constant 2nd & 3rd parameters SUBSTR can only have a constant 2nd & 3rd parameters LEFT can only have a constant 2nd parameter RIGHT can only have a constant 2nd paramater, and the first paramater must be a field or constant only. IIF must return a predictable length and type TRIM, LTRIM & ALLTRIM return unpredictable lengths. Its result Ex. TRIM(L_NAME) + TRIM(F_NAME) is OK SUBSTR( TRIM(L_NAME), 3, 2 ) is not OK Memo field's evaluate to a maximum length. Anything over this maximum gets truncated. */ #include "d4all.h" #ifdef __TURBOC__ #pragma hdrstop #endif #include static void e4functionPop( EXPR4 * ) ; /* e4massage - Check the type returns to ensure that functions get the correct type result. Use 'E4FUNCTIONS.code' to change the function where possible so that the correct function is used. - Make sure that field parameters are put on the stack for the concatentate operators. - Fill in the function pointers. - Change (FIELD4 *) pointers in 'p1' to (char *) pointers. - Where the result stack is used, make sure the correct values are filled into the E4INFO entires in order to adjust for the lengths needed. - Check the length returns to make sure that 'codeBase->exprBufLen' is large enough to handle executing the expression. - Calculate the length of the final result. - Enforce restrictions to TRIM, STR and IIF - Make sure an extra max. length character is added for e4upper() & e4trim() */ static int e4massage( E4PARSE *p4 ) { E4INFO *info ; int parmPos, iParm, isOk, codeOn, iInfo, numParms ; int typeShouldBe, len, lengthStatus, i, doneTrimMemoOrCalc ; int position[E4MAX_STACK_ENTRIES+1] ; long length[E4MAX_STACK_ENTRIES] ; long bufLenNeeded ; int types[E4MAX_STACK_ENTRIES] ; int numEntries[E4MAX_STACK_ENTRIES] ; E4INFO *pointers[E4MAX_STACK_ENTRIES] ; CODE4 *codeBase ; unsigned storedKeyLen ; #ifdef S4DATA_ALIGN int delta ; #endif #ifdef E4PARM_LOW if ( p4 == 0 ) return error4( 0, e4parm_null, E90901 ) ; #endif #ifdef E4MISC memset( types, 0, sizeof( types ) ) ; memset( pointers, 0, sizeof( pointers ) ) ; memset( length, 0, sizeof( length ) ) ; memset( numEntries, 0, sizeof( numEntries ) ) ; memset( position, 0, sizeof( position ) ) ; #endif codeBase = p4->codeBase ; numParms = doneTrimMemoOrCalc = 0 ; bufLenNeeded = 0 ; position[0] = 0 ; /* The first parameter can be placed at position 0 */ for( iInfo = 0; iInfo < p4->expr.infoN; iInfo++ ) { info = p4->expr.info + iInfo ; /* Check the parameters types */ codeOn = v4functions[info->functionI].code ; if ( v4functions[info->functionI].numParms != (char)info->numParms ) if ( v4functions[info->functionI].numParms > 0 ) { if( codeBase->errExpr ) return error4describe( codeBase, e4numParms, E90901, p4->expr.source, 0, 0 ) ; return e4numParms ; } for( ;; ) { if ( codeOn != v4functions[info->functionI].code ) { if( codeBase->errExpr ) return error4describe( codeBase, e4typeSub, E90901, p4->expr.source, 0, 0 ) ; return e4typeSub ; } isOk = 1 ; for( iParm = 0; iParm < info->numParms; iParm++ ) { if ( (int)v4functions[info->functionI].numParms < 0 ) typeShouldBe = v4functions[info->functionI].type[0] ; else typeShouldBe = v4functions[info->functionI].type[iParm] ; parmPos = numParms - info->numParms + iParm ; if ( types[parmPos] != typeShouldBe ) { if ( types[parmPos] == r4date && typeShouldBe == r4dateDoub ) { pointers[parmPos]->functionI = E4FIELD_DATE_D ; length[parmPos] = sizeof(double) ; continue ; } if ( types[parmPos] == r4num && typeShouldBe == r4numDoub ) { pointers[parmPos]->functionI = E4FIELD_NUM_D ; length[parmPos] = sizeof(double) ; continue ; } #ifdef S4CLIENT_OR_FOX if ( types[parmPos] == r4int && typeShouldBe == r4numDoub ) { pointers[parmPos]->functionI = E4FIELD_INT_D ; length[parmPos] = sizeof(double) ; continue ; } if ( types[parmPos] == r4currency && typeShouldBe == r4numDoub ) { pointers[parmPos]->functionI = E4FIELD_CUR_D ; length[parmPos] = sizeof(double) ; continue ; } #endif info->functionI++ ; isOk = 0 ; break ; } } if ( isOk ) break ; } switch( info->functionI ) { case E4CONCATENATE: case E4CONCAT_TWO: case E4TRIM: case E4LTRIM: case E4ALLTRIM: case E4UPPER: case E4SUBSTR: case E4LEFT: case E4RIGHT: case E4DESCEND: case E4DESCEND+1: case E4DESCEND+2: case E4DESCEND+3: case E4DESCEND+4: case E4DESCEND+5: case E4DESCEND+6: case E4ASCEND: case E4ASCEND+1: case E4ASCEND+2: case E4ASCEND+3: case E4ASCEND+4: case E4ASCEND+5: case E4ASCEND+6: #ifndef S4MEMO_OFF case E4FIELD_MEMO: #endif for( iParm = 1; iParm <= info->numParms; iParm++ ) { E4INFO *info_parm = pointers[numParms-iParm] ; if ( info_parm->functionI == E4FIELD_STR ) /* Make sure the parameter is put on the stack. */ info_parm->functionI = E4FIELD_STR_CAT ; if ( info->functionI == E4CONCATENATE && doneTrimMemoOrCalc ) info->functionI = E4CONCAT_TRIM ; } break ; default: break ; } numParms -= info->numParms ; if ( numParms < 0 ) if( codeBase->errExpr ) return error4( codeBase, e4result, E90901 ) ; types[numParms] = v4functions[info->functionI].returnType ; if ( info->functionI == E4CALC_FUNCTION ) types[numParms] = expr4type( ((EXPR4CALC *) info->p1)->expr ) ; switch( types[numParms] ) { case r4str: switch( info->functionI ) { case E4FIELD_STR: case E4FIELD_STR_CAT: length[numParms] = f4len( info->fieldPtr ) ; break ; #ifndef S4MEMO_OFF case E4FIELD_MEMO: length[numParms] = codeBase->memSizeMemoExpr ; doneTrimMemoOrCalc = 1 ; break ; #endif /* S4MEMO_OFF */ case E4CONCATENATE: case E4CONCAT_TWO: case E4CONCAT_TRIM: info->i1 = (int) (length[numParms]) ; length[numParms] += length[numParms+1] ; break ; case E4IIF: if ( length[numParms+1] != length[numParms+2] ) if( codeBase->errExpr ) return error4describe( codeBase, e4lengthErr, E90901, p4->expr.source, 0, 0 ) ; length[numParms] = length[numParms+1] ; break ; case E4DTOS: case E4DTOS+1: length[numParms] = sizeof(double) ; break ; case E4DTOC: case E4DTOC+1: case E4CTOD: length[numParms] = sizeof(double) ; info->i1 = p4->constants.pos ; len = strlen( code4dateFormat( codeBase ) ) ; s4stackPushStr( &p4->constants, code4dateFormat( codeBase ), len + 1 ) ; if ( info->functionI == E4DTOC || info->functionI == E4DTOC+1 ) length[numParms] = len ; break ; case E4CHR: length[numParms] = sizeof(char) ; break ; case E4DEL: length[numParms] = sizeof(char) ; #ifndef S4CLIENT if ( p4->expr.tagPtr ) /* data4 independent, so point to datafile */ info->p1 = (char *)&p4->expr.dataFile->record ; else /* data4 dependent, so just point to record */ #endif info->p1 = (char *)&p4->expr.data->record ; break ; case E4CALC_FUNCTION: doneTrimMemoOrCalc = 1 ; length[numParms] = expr4len( ((EXPR4CALC *) info->p1)->expr ) ; break ; case E4RIGHT: if ( info->i1 > (int)(length[numParms]) ) info->i1 = (int)(length[numParms]) ; if ( info->i1 < 0 ) info->i1 = 0 ; if ( info->len > (int)(length[numParms]) ) info->len = (int)(length[numParms]) ; length[numParms] = info->len ; break ; case E4SUBSTR: case E4LEFT: if ( info->i1 > (int)(length[numParms]) ) info->i1 = (int)(length[numParms]) ; if ( info->i1 < 0 ) info->i1 = 0 ; length[numParms] -= info->i1 ; if ( info->len > (int)(length[numParms]) ) info->len = (int)(length[numParms]) ; length[numParms] = info->len ; break ; case E4TIME: length[numParms] = sizeof(double) ; break ; case E4TRIM: case E4LTRIM: case E4ALLTRIM: doneTrimMemoOrCalc = 1 ; p4->expr.hasTrim = 1 ; break ; case E4UPPER: break ; case E4DESCEND: case E4DESCEND+1: case E4DESCEND+2: case E4DESCEND+3: case E4DESCEND+4: #ifdef S4CLIENT_OR_FOX case E4DESCEND+5: case E4DESCEND+6: #endif case E4ASCEND: case E4ASCEND+1: case E4ASCEND+2: case E4ASCEND+3: case E4ASCEND+4: #ifdef S4CLIENT_OR_FOX case E4ASCEND+5: case E4ASCEND+6: #endif if( types[numParms] == r4dateDoub || types[numParms] == r4numDoub || types[numParms] == r4dateTime ) length[numParms] = sizeof(double) ; if( types[numParms] == r4log ) length[numParms] = sizeof(char) ; break ; default: length[numParms] = info->len ; } break ; case r4num: length[numParms] = f4len( info->fieldPtr ) ; break ; #ifdef S4CLIENT_OR_FOX case r4currency: length[numParms] = sizeof(double) ; break ; case r4dateTime: length[numParms] = sizeof(double) ; break ; case r4int: length[numParms] = sizeof(long) ; break ; #endif case r4numDoub: case r4dateDoub: length[numParms] = sizeof(double) ; if ( info->functionI == E4CTOD ) { info->i1 = p4->constants.pos ; s4stackPushStr( &p4->constants, code4dateFormat( codeBase ), strlen( code4dateFormat( codeBase ) ) + 1 ) ; } if ( info->functionI == E4RECCOUNT ) info->p1 = (char *)p4->expr.dataFile ; if ( info->functionI == E4RECNO ) info->p1 = 0 ; /* updated for c/s support, just use expr data4 */ break ; case r4date: length[numParms] = sizeof(double) ; break ; case r4log: if ( info->functionI != E4FIELD_LOG ) { if ( info->functionI == E4DELETED ) #ifndef S4CLIENT if ( p4->expr.tagPtr ) /* data4 independent, so point to datafile */ info->p1 = (char *)&p4->expr.dataFile->record ; else /* data4 dependent, so just point to record */ #endif info->p1 = (char *)&p4->expr.data->record ; else { info->i1 = (int)(length[numParms+1]) ; lengthStatus = 1 ; if ( length[numParms] < length[numParms+1] ) { info->i1 = (int)(length[numParms]) ; lengthStatus = -1 ; } if ( length[numParms] == length[numParms+1] ) lengthStatus = 0 ; if ( info->functionI == E4GREATER ) { if ( lengthStatus > 0 ) info->p1 = (char *)1L ; else info->p1 = (char *)0L ; } if ( info->functionI == E4LESS ) { if ( lengthStatus < 0 ) info->p1 = (char *)1L ; else info->p1 = (char *)0L ; } } } length[numParms] = sizeof(int) ; break ; default: return error4( codeBase, e4result, E90901 ) ; } /* make sure there is enough key space allocated for the type, in case a partial evaluation occurs */ #ifdef S4CLIENT storedKeyLen = (unsigned)(length[numParms]) ; #else switch( types[numParms] ) { #ifdef S4FOX case r4num: case r4date: case r4numDoub: storedKeyLen = sizeof( double ) ; break ; #endif /* ifdef S4FOX */ #ifdef S4CLIPPER case r4num: /* numeric field return, must fix length problem */ storedKeyLen = f4len( info->fieldPtr ) ; break ; case r4numDoub: storedKeyLen = codeBase->numericStrLen ; break ; #endif /* ifdef S4CLIPPER */ #ifdef S4NDX case r4num: case r4date: storedKeyLen = sizeof( double ) ; break ; #endif /* ifdef S4NDX */ #ifdef S4MDX case r4num: storedKeyLen = (int)sizeof( C4BCD ) ; break ; case r4numDoub: storedKeyLen = (int)sizeof( C4BCD ) ; break ; case r4date: case r4dateDoub: storedKeyLen = sizeof( double ) ; break ; #endif /* S4MDX */ default: storedKeyLen = (unsigned)(length[numParms]) ; } #ifdef S4FOX storedKeyLen++ ; /* null entry will increase length by one */ #endif #endif u4allocAgain( codeBase, &codeBase->storedKey, &codeBase->storedKeyLen, (unsigned)storedKeyLen + 1 ) ; if ( error4code( codeBase ) < 0 ) return -1 ; #ifdef S4DATA_ALIGN delta = 0 ; switch(types[numParms]) { case r4numDoub: case r4dateDoub: case r4num: case r4date: { int rem ; if ((rem=position[numParms]%sizeof(double))) delta=sizeof(double)-rem; } } info->resultPos=position[numParms]+delta; #else info->resultPos = position[numParms] ; #endif bufLenNeeded = length[numParms] ; if ( info->functionI == E4CALC_FUNCTION ) bufLenNeeded = ((EXPR4CALC *)info->p1)->expr->lenEval ; /* bufLenNeeded = ((EXPR4 *)info->p1)->lenEval ; */ if( bufLenNeeded > INT_MAX ) return error4( codeBase, e4overflow, E90901 ) ; if( (types[numParms] == r4num || types[numParms] == r4date || types[numParms] == r4int) && length[numParms] < sizeof(double) ) position[numParms+1] = info->resultPos + sizeof(double) ; else position[numParms+1] = info->resultPos + (unsigned)length[numParms] ; if ( info->resultPos + bufLenNeeded > (long)p4->expr.lenEval ) p4->expr.lenEval = info->resultPos + (unsigned)bufLenNeeded ; info->len = (int)(length[numParms]) ; info->numEntries = 1 ; for( i = 0; i < info->numParms; i++ ) info->numEntries += numEntries[numParms+i] ; numEntries[numParms] = info->numEntries ; pointers[numParms] = info ; numParms++ ; if ( numParms >= E4MAX_STACK_ENTRIES ) if( codeBase->errExpr ) return error4( codeBase, e4overflow, E90901 ) ; } if ( numParms != 1 ) { if( codeBase->errExpr ) error4( codeBase, e4result, E90901 ) ; return -1 ; } for( i = 0; i < p4->expr.infoN; i++ ) { info = p4->expr.info + i ; info->function = (S4OPERATOR *)v4functions[info->functionI].functionPtr ; } p4->expr.lenEval += 1 ; if ( codeBase->exprBufLen < (unsigned)p4->expr.lenEval ) if ( u4allocAgain( codeBase, &codeBase->exprWorkBuf, &codeBase->exprBufLen, p4->expr.lenEval ) == e4memory ) return error4stack( codeBase, e4memory, E90901 ) ; p4->expr.len = (int)(length[0]) ; p4->expr.type = types[0] ; return 0 ; } int e4addConstant( E4PARSE *p4, const int iFunctions, const void *consPtr, const unsigned consLen ) { E4INFO *info ; #ifdef E4PARM_LOW if ( p4 == 0 ) return error4( 0, e4parm_null, E90902 ) ; #endif info = e4functionAdd( &p4->expr, iFunctions ) ; if ( info == 0 ) return -1 ; info->i1 = p4->constants.pos ; info->len = consLen ; return s4stackPushStr( &p4->constants, consPtr, (int)consLen ) ; } E4INFO *e4functionAdd( EXPR4 *expr, const int iFunctions ) { E4INFO *info ; #ifdef E4PARM_LOW if ( expr == 0 ) { error4( 0, e4parm_null, E90903 ) ; return 0 ; } #endif if ( (unsigned)((expr->infoN+1)*sizeof(E4INFO)) > expr->codeBase->exprBufLen ) if ( u4allocAgain( expr->codeBase, &expr->codeBase->exprWorkBuf, &expr->codeBase->exprBufLen, sizeof(E4INFO) * (expr->infoN+10) ) == e4memory ) return 0 ; info = (E4INFO *)expr->codeBase->exprWorkBuf + expr->infoN++ ; info->functionI = iFunctions ; info->numParms = v4functions[iFunctions].numParms ; if ( info->numParms < 0 ) info->numParms = 2 ; info->function = (S4OPERATOR *)v4functions[iFunctions].functionPtr ; return info ; } static void e4functionPop( EXPR4 *expr ) { expr->infoN-- ; } EXPR4 *S4FUNCTION expr4parseLow( DATA4 *d4, const char *exprPtr, TAG4FILE *tagPtr ) { E4PARSE parse ; char ops[128] ; char constants[512] ; char *src ; int rc, infoLen, posConstants ; EXPR4 *express4 ; #ifdef E4PARM_HIGH if ( d4 == 0 || exprPtr == 0 ) { error4( 0, e4parm_null, E90904 ) ; return 0 ; } #endif if ( error4code( d4->codeBase ) < 0 ) return 0 ; if ( d4->codeBase->exprBufLen > 0 ) memset( d4->codeBase->exprWorkBuf, 0, d4->codeBase->exprBufLen ) ; memset( (void *)&parse, 0, sizeof(E4PARSE) ) ; memset( ops, 0, sizeof(ops)); parse.expr.tagPtr = tagPtr ; #ifdef S4FOX parse.expr.vfpInfo = tagPtr ? &tagPtr->vfpInfo : 0 ; #endif parse.expr.data = d4 ; parse.expr.source = (char *)exprPtr ; parse.codeBase = d4->codeBase ; parse.expr.codeBase = d4->codeBase ; parse.op.ptr = ops ; parse.op.len = sizeof(ops) ; parse.op.codeBase = d4->codeBase ; parse.constants.ptr = constants ; parse.constants.len = sizeof(constants) ; parse.constants.codeBase = d4->codeBase ; s4scanInit( &parse.scan, (unsigned char *)exprPtr ) ; rc = expr4parseExpr( &parse ) ; if ( rc < 0 ) return 0 ; if ( s4stackCur( &parse.op ) != E4NO_FUNCTION ) { if( parse.codeBase->errExpr ) error4describe( parse.codeBase, e4complete, E90904, exprPtr, 0, 0 ) ; return 0 ; } parse.expr.info = (E4INFO *)parse.codeBase->exprWorkBuf ; parse.expr.dataFile = d4->dataFile ; if ( e4massage( &parse ) < 0 ) return 0 ; infoLen = parse.expr.infoN * sizeof(E4INFO) ; posConstants = sizeof(EXPR4) + infoLen ; express4 = (EXPR4 *)u4allocFree( d4->codeBase, (long)posConstants + parse.constants.len + parse.scan.len + 1L ) ; if ( express4 == 0 ) return 0 ; memcpy( (void *)express4, (void *)&parse.expr, sizeof(EXPR4) ) ; express4->data = d4 ; express4->dataFile = d4->dataFile ; express4->info = (E4INFO *)( express4 + 1 ) ; express4->constants = (char *) express4 + posConstants ; src = express4->constants + parse.constants.len ; express4->source = src ; memcpy( (void *)express4->info, parse.codeBase->exprWorkBuf, (unsigned int)infoLen ) ; memcpy( express4->constants, constants, parse.constants.len ) ; strcpy( src, exprPtr ) ; #ifdef S4CLIPPER express4->keyLen = parse.expr.keyLen ; express4->keyDec = parse.expr.keyDec ; #endif return express4 ; } /*EXPR4 *S4FUNCTION expr4parse( DATA4 *d4, char *exprPtr )*/ /*{*/ /* return expr4parseLow( d4, exprPtr, 0 ) ;*/ /*}*/ /* Looks at the input string and returns and puts a character code on the result stack corresponding to the next operator. The operators all operate on two operands. Ex. +,-,*,/, >=, <, .AND., ... If the operator is ambiguous, return the arithmatic choice. Returns -2 (Done), 0, -1 (Error) */ int e4getOperator( E4PARSE *p4, int *opReturn) { char ch ; int op ; #ifdef E4PARM_LOW if ( p4 == 0 || opReturn == 0 ) return error4( 0, e4parm_null, E90505 ) ; #endif s4scanRange( &p4->scan, 1, ' ' ) ; ch = s4scanChar(&p4->scan) ; if ( ch==0 || ch==')' || ch==',' ) { *opReturn = E4DONE ; return 0 ; } op = e4lookup( p4->scan.ptr+p4->scan.pos, -1, E4FIRST_OPERATOR, E4LAST_OPERATOR ) ; if ( op < 0 ) if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4unrecOperator, E90905, (char *)p4->scan.ptr, 0, 0 ) ; p4->scan.pos += v4functions[op].nameLen ; *opReturn = op ; return 0 ; } /* e4lookup, searches 'v4functions' for an operator or function. str - the function name strLen - If 'strLen' is greater than or equal to zero it contains the exact number of characters in 'str' to lookup. Otherwise, as many as needed, provided an ending null is not reached, are compared. */ int S4FUNCTION e4lookup( const unsigned char *str, const int strLen, const int startI, const int endI ) { char uStr[9] ; /* Maximum # of function name characters plus one. */ int i ; #ifdef E4PARM_LOW if ( str == 0 || endI < startI ) return error4( 0, e4parm, E90906 ) ; #endif u4ncpy( uStr, (char *)str, sizeof( uStr ) ) ; c4upper( uStr ) ; for( i=startI; i<= endI; i++) { if ( v4functions[i].code < 0 ) break ; if ( v4functions[i].name == 0 ) continue ; #ifdef E4ANALYZE if ( v4functions[i].nameLen >= (char)sizeof( uStr ) ) return error4( 0, e4result, E90906 ) ; #endif if ( v4functions[i].name[0] == uStr[0] ) if( strLen == v4functions[i].nameLen || strLen < 0 ) if ( strncmp( uStr, v4functions[i].name, (size_t)v4functions[i].nameLen ) == 0 ) return i ; } return -1 ; } static int opToExpr( E4PARSE *p4 ) { E4INFO *info ; #ifdef E4PARM_LOW if ( p4 == 0 ) return error4( 0, e4parm_null, E90907 ) ; #endif info = e4functionAdd( &p4->expr, s4stackPop(&p4->op) ) ; if ( info == 0 ) return -1 ; for( ; s4stackCur(&p4->op) == E4ANOTHER_PARM ; ) { s4stackPop(&p4->op) ; info->numParms++ ; } return 0 ; } /* Parses an expression consisting of value [[operator value] ...] The expression is ended by a ')', a ',' or a '\0'. Operators are only popped until a '(', a ',' or the start of the stack. Left to right evaluation for operators of equal priority. An ambiguous operator is one which can be interpreted differently depending on its operands. However, its operands depend on the priority of the operators and the evaluation order. Fortunately, the priority of an ambigous operator is constant regardless of its interpretation. Consequently, the evaluation order is determined first. Then ambiguous operators can be exactly determined. Ambigous operators:+, -, >, <, <=, >=, =, <>, # Return 0 Normal -1 Error */ int expr4parseExpr( E4PARSE *p4 ) { int rc, opValue, opOnStack ; #ifdef E4PARM_LOW if ( p4 == 0 ) return error4( 0, e4parm_null, E90908 ) ; #endif rc = expr4parseValue( p4 ) ; if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90908 ) ; for( ;; ) { rc = e4getOperator( p4, &opValue ) ; if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90908 ) ; if ( opValue == E4DONE ) /* Done */ { while( s4stackCur(&p4->op) != E4L_BRACKET && s4stackCur(&p4->op) != E4COMMA && s4stackCur(&p4->op) != E4NO_FUNCTION ) { rc = opToExpr( p4 ) ; if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90908 ) ; } return 0 ; } /* Everything with a higher or equal priority than 'opValue' must be executed first. (equal because of left to right evaluation order) Consequently, all high priority operators are sent to the result stack. */ while ( s4stackCur( &p4->op ) >= 0 ) { opOnStack = s4stackCur(&p4->op ) ; if ( v4functions[opValue].priority <= v4functions[opOnStack].priority ) { if ( opValue == opOnStack && (int)v4functions[opValue].numParms < 0 ) { /* If repeated AND or OR operator, combine them into one with an extra paramter. This makes the relate module optimization algorithms easier. */ s4stackPop( &p4->op ) ; s4stackPushInt( &p4->op, E4ANOTHER_PARM ) ; break ; } else { rc = opToExpr( p4 ) ; if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90908 ) ; } } else break ; } s4stackPushInt( &p4->op, opValue ) ; rc = expr4parseValue( p4 ) ; if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90908 ) ; } } int expr4parseFunction( E4PARSE *p4, const char *startPtr, const int fLen ) { int fNum, numParms, infoI1, infoLen, rc, rVal ; char ch ; #ifdef S4DATA_ALIGN double doubVal ; #endif E4INFO *info, *rInfo ; void *newOrTotalPtr = 0 ; EXPR4CALC *calc ; #ifdef E4PARM_LOW if ( p4 == 0 || startPtr == 0 || fLen < 0 ) return error4( 0, e4parm, E90909 ) ; #endif infoI1 = infoLen = 0 ; if ( error4code( p4->codeBase ) < 0 ) return e4codeBase ; fNum = e4lookup( (unsigned char *)startPtr, fLen, E4FIRST_FUNCTION, 0x7FFF) ; if( fNum < 0 ) { newOrTotalPtr = calc = expr4calcLookup( p4->codeBase, startPtr, fLen ) ; if( calc == 0 ) { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4unrecFunction, E90909, (char *)p4->scan.ptr, 0, 0 ) ; return e4unrecFunction ; } else { fNum = E4CALC_FUNCTION ; #ifndef S4SERVER if( calc->total != 0 ) { fNum = E4TOTAL ; newOrTotalPtr = calc->total ; } #endif } } s4stackPushInt( &p4->op, E4L_BRACKET ) ; p4->scan.pos++ ; numParms = 0 ; for( ;; ) { ch = s4scanChar( &p4->scan ) ; if ( ch == 0 ) { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4rightMissing, E90909, (char *)p4->scan.ptr, 0, 0 ) ; return e4rightMissing ; } if ( ch == ')') { p4->scan.pos++ ; break ; } rc = expr4parseExpr( p4 ) ; if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90909 ) ; numParms++ ; while( s4scanChar( &p4->scan ) <= ' ' && s4scanChar( &p4->scan ) >='\1') p4->scan.pos++ ; if ( s4scanChar( &p4->scan ) == ')') { p4->scan.pos++ ; break ; } if ( s4scanChar( &p4->scan ) != ',') { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4commaExpected, E90909, (char *)p4->scan.ptr, 0, 0 ) ; return e4commaExpected ; } p4->scan.pos++ ; } s4stackPop( &p4->op ) ; /* pop the left bracket */ if ( fNum == E4STR ) { infoLen= 10 ; if ( numParms == 3 ) { info = (E4INFO *) p4->codeBase->exprWorkBuf + p4->expr.infoN -1 ; if ( info->functionI != E4DOUBLE ) { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4notConstant, E90909, p4->expr.source, 0, 0 ) ; return e4notConstant ; } #ifdef S4DATA_ALIGN memcpy( (void *)&doubVal, (p4->constants.ptr + info->i1), sizeof(double) ) ; infoI1 = (int) doubVal ; #else infoI1 = (int) *(double *) (p4->constants.ptr + info->i1) ; #endif e4functionPop( &p4->expr ) ; numParms-- ; } if ( numParms == 2 ) { info = (E4INFO *)p4->codeBase->exprWorkBuf + p4->expr.infoN - 1 ; if ( info->functionI != E4DOUBLE ) { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4notConstant, E90909, p4->expr.source, 0, 0 ) ; return e4notConstant ; } #ifdef S4DATA_ALIGN memcpy( (void *)&doubVal, (p4->constants.ptr + info->i1), sizeof(double) ) ; infoLen = (int) doubVal ; #else infoLen = (int) *(double *) (p4->constants.ptr + info->i1) ; #endif e4functionPop( &p4->expr ) ; numParms-- ; } if ( infoLen < 0 ) infoLen = 10 ; if ( infoLen <= infoI1+1 ) infoI1 = infoLen - 2 ; if ( infoI1 < 0 ) infoI1 = 0 ; } if ( numParms == 2 && fNum == E4RIGHT ) { info = (E4INFO *)p4->codeBase->exprWorkBuf + p4->expr.infoN -1 ; rInfo = info - 1 ; /* get the constant/field len */ if ( info->functionI != E4DOUBLE ) { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4notConstant, E90909, p4->expr.source, 0, 0 ) ; return e4notConstant ; } #ifdef S4DATA_ALIGN memcpy( (void *)&doubVal, (p4->constants.ptr + info->i1), sizeof(double) ) ; infoI1 = (int) doubVal ; #else infoI1 = (int) *(double *) (p4->constants.ptr + info->i1) ; #endif if ( rInfo->fieldPtr != 0 ) /* is a field */ rVal = f4len( rInfo->fieldPtr ) - infoI1 ; else /* assume constant */ rVal = rInfo->len - infoI1 ; infoLen = infoI1 ; infoI1 = rVal ; if ( infoLen < 0 ) infoLen = 0 ; e4functionPop( &p4->expr ) ; numParms-- ; } if ((numParms == 3 && fNum == E4SUBSTR) || (numParms == 2 && fNum == E4LEFT)) { info = (E4INFO *)p4->codeBase->exprWorkBuf + p4->expr.infoN -1 ; if ( info->functionI != E4DOUBLE ) { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4notConstant, E90909, p4->expr.source, 0, 0 ) ; return e4notConstant ; } #ifdef S4DATA_ALIGN memcpy( (void *)&doubVal, (p4->constants.ptr + info->i1), sizeof(double) ) ; infoLen = (int) doubVal ; #else infoLen = (int) *(double *) (p4->constants.ptr + info->i1) ; #endif e4functionPop( &p4->expr ) ; numParms-- ; } if ( numParms == 2 && fNum == E4SUBSTR ) { info = (E4INFO *) p4->codeBase->exprWorkBuf + p4->expr.infoN -1 ; if ( info->functionI != E4DOUBLE ) { if( p4->codeBase->errExpr ) error4describe( p4->codeBase, e4notConstant, E90909, p4->expr.source, 0, 0 ) ; return e4notConstant ; } #ifdef S4DATA_ALIGN memcpy( (void *)&doubVal, (p4->constants.ptr + info->i1), sizeof(double) ) ; infoI1 = (int) doubVal ; #else infoI1 = (int) *(double *) (p4->constants.ptr + info->i1) ; #endif infoI1-- ; e4functionPop( &p4->expr ) ; numParms-- ; } if ( error4code( p4->codeBase ) < 0 ) return -1 ; if ( numParms != v4functions[fNum].numParms && (int)v4functions[fNum].numParms >= 0 ) { if( fNum == E4DTOC && numParms == 2 ) { e4functionPop( &p4->expr ) ; numParms-- ; fNum = E4DTOS ; } else { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4numParms, E80906, v4functions[fNum].name, (char *)p4->scan.ptr, 0 ) ; return e4numParms ; } } info = e4functionAdd( &p4->expr, fNum ) ; if ( info == 0 ) return -1 ; info->i1 = infoI1 ; info->len = infoLen ; info->numParms = numParms ; if ( fNum == E4CALC_FUNCTION || fNum == E4TOTAL ) info->p1 = (char *)newOrTotalPtr ; return 0 ; } int expr4parseValue( E4PARSE *p4 ) { FIELD4 *fieldPtr ; char ch, searchChar ; const unsigned char *startPtr ; int rc, iFunctions, len, iFunction, savePos ; double d ; E4INFO *expr, *info ; DATA4 *basePtr ; char bName[11], fName[11] ; #ifdef E4PARM_LOW if ( p4 == 0 ) return error4( 0, e4parm_null, E90910 ) ; #endif if ( error4code( p4->codeBase ) < 0 ) return e4codeBase ; s4scanRange( &p4->scan, ' ', ' ' ) ; /* expression */ if ( s4scanChar( &p4->scan ) == '(') { p4->scan.pos++ ; s4stackPushInt( &p4->op, E4L_BRACKET) ; rc = expr4parseExpr( p4 ) ; if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90910 ) ; while ( s4scanChar( &p4->scan ) <= ' ' && s4scanChar( &p4->scan ) != 0) p4->scan.pos++ ; if ( s4scanChar( &p4->scan ) != ')' ) { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4rightMissing, E90910, (char *)p4->scan.ptr, 0, 0 ) ; return e4rightMissing ; } p4->scan.pos++ ; s4stackPop( &p4->op ) ; return 0 ; } /* logical */ if ( s4scanChar( &p4->scan ) == '.' ) { iFunctions = e4lookup( p4->scan.ptr+p4->scan.pos, -1, E4FIRST_LOG, E4LAST_LOG ) ; if ( iFunctions >= 0 ) { p4->scan.pos += v4functions[iFunctions].nameLen ; if ( strcmp( v4functions[iFunctions].name, ".NOT." ) == 0 ) { rc = expr4parseValue( p4 ) ; /* One operand operation */ if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90910 ) ; s4stackPushInt( &p4->op, iFunctions ) ; return 0 ; } expr = e4functionAdd( &p4->expr, iFunctions ) ; if ( expr == 0 ) return -1 ; return 0 ; } } /* string */ ch = s4scanChar( &p4->scan ) ; if ( ch == '\'' || ch == '\"' || ch == '[' ) { if ( ch == '[' ) searchChar = ']' ; else searchChar = ch ; p4->scan.pos++ ; startPtr = p4->scan.ptr + p4->scan.pos ; len = s4scanSearch( &p4->scan, searchChar ) ; if ( s4scanChar( &p4->scan ) != searchChar ) if ( len < 0 ) { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4unterminated, E90910, (char *)p4->scan.ptr, 0, 0 ) ; return e4unterminated ; } p4->scan.pos++ ; rc = e4addConstant( p4, E4STRING, startPtr, (unsigned int)len ) ; if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90910 ) ; return 0 ; } /* real */ ch = s4scanChar( &p4->scan ) ; if ( ((ch >='0') && (ch <='9')) || (ch == '-') || (ch == '+') || (ch == '.') ) { startPtr = p4->scan.ptr + p4->scan.pos ; savePos = p4->scan.pos ; p4->scan.pos++ ; len = 1 ; while( ((s4scanChar( &p4->scan ) >= '0') && (s4scanChar( &p4->scan ) <= '9')) || (s4scanChar( &p4->scan ) == '.') ) { if ( s4scanChar( &p4->scan ) == '.' ) { if ( strnicmp( (char *)p4->scan.ptr + p4->scan.pos, ".AND.", 5) == 0 || strnicmp( (char *)p4->scan.ptr + p4->scan.pos, ".OR.", 4) == 0 || strnicmp( (char *)p4->scan.ptr + p4->scan.pos, ".NOT.", 5) == 0 ) break ; /* if the next value is a character, then we have a database with a number as its name/alias. (i.e. 111.afld), since numerics are invalid to being a field name, MUST be a number if a numeric after the decimal point... */ if ( toupper( s4scanChar( &p4->scan + 1 ) ) >= 'A' && toupper( s4scanChar( &p4->scan + 1 ) ) <= 'Z' ) { p4->scan.pos++ ; /* make sure a letter is identified below... */ break ; } } len++ ; p4->scan.pos++ ; } /* check to see if maybe actually a database name starting with a numeric... */ if ( toupper( s4scanChar( &p4->scan ) ) >= 'A' && toupper( s4scanChar( &p4->scan ) ) <= 'Z' ) p4->scan.pos = savePos ; else { d = c4atod( (char *)startPtr, len ) ; rc = e4addConstant( p4, E4DOUBLE, &d, sizeof(d) ) ; if ( rc < 0 ) return error4stack( p4->codeBase, (short)rc, E90910 ) ; return 0 ; } } /* function or field */ if ( u4nameChar( s4scanChar( &p4->scan ) ) ) { startPtr = p4->scan.ptr + p4->scan.pos ; for( len = 0 ; u4nameChar( s4scanChar( &p4->scan ) ) ; len++ ) p4->scan.pos++ ; s4scanRange( &p4->scan, (char)0, ' ' ) ; if ( s4scanChar( &p4->scan ) == '(' ) return expr4parseFunction( p4, (char *)startPtr, len ) ; basePtr = 0 ; #ifdef S4FOX if ( s4scanChar( &p4->scan ) == '.' ) { /* for fox, same as -> */ if ( len > 10 ) len = 10 ; c4memmove( bName, startPtr, (size_t)len ) ; bName[len] = '\0' ; basePtr = tran4dataName( code4trans( p4->codeBase ), bName, 0L, 1 ) ; } #endif if ( s4scanChar( &p4->scan ) == '-' ) if ( p4->scan.ptr[p4->scan.pos+1] == '>') { if ( len > 10 ) len = 10 ; c4memmove( bName, startPtr, (size_t)len ) ; bName[len] = '\0' ; basePtr = tran4dataName( code4trans( p4->codeBase ), bName, 0L, 1 ) ; if ( basePtr == 0 ) { if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4dataName, E90910, bName, (char *)p4->scan.ptr, (char *) 0 ) ; return e4dataName ; } p4->scan.pos++ ; } if ( basePtr != 0 ) { if ( p4->expr.tagPtr ) /* data4 independent, so point to datafile */ return error4( p4->codeBase, e4tagExpr, E80909 ) ; p4->scan.pos++ ; startPtr = p4->scan.ptr + p4->scan.pos ; for( len = 0 ; u4nameChar( s4scanChar( &p4->scan ) ) ; len++ ) p4->scan.pos++ ; } else basePtr = (DATA4 *)p4->expr.data ; if ( len <= 10) { c4memmove( fName, startPtr, (size_t)len ) ; fName[len] = 0 ; fieldPtr = d4field( basePtr, fName ) ; if ( fieldPtr == 0 ) return -1 ; #ifdef S4CLIPPER p4->expr.keyLen = fieldPtr->len ; p4->expr.keyDec = fieldPtr->dec ; #endif iFunction = 0 ; switch( fieldPtr->type ) { case r4num: case r4float: iFunction = E4FIELD_NUM_S ; break ; case r4str: iFunction = E4FIELD_STR ; break ; case r4date: iFunction = E4FIELD_DATE_S ; break ; case r4log: iFunction = E4FIELD_LOG ; break ; case r4memo: #ifdef S4MEMO_OFF return error4( p4->codeBase, e4notMemo, E90910 ) ; #else iFunction = E4FIELD_MEMO ; break ; #endif #ifdef S4CLIENT_OR_FOX /* visual Fox 3.0 new field types */ case r4currency: iFunction = E4FIELD_CUR ; break ; case r4dateTime: iFunction = E4FIELD_DTTIME ; break ; case r4double: iFunction = E4FIELD_DOUB ; break ; case r4int: iFunction = E4FIELD_INT ; break ; #endif default: if( p4->codeBase->errExpr ) return error4( p4->codeBase, e4typeSub, E80901 ) ; return -1 ; } info = e4functionAdd( &p4->expr, iFunction ) ; if ( info == 0 ) return -1 ; info->fieldNo = f4number( fieldPtr ) ; info->fieldPtr = fieldPtr ; #ifndef S4CLIENT if ( p4->expr.tagPtr ) info->p1 = (char *)&basePtr->dataFile->record ; else #endif info->p1 = (char *)&basePtr->record ; info->i1 = fieldPtr->offset ; return 0 ; } } if( p4->codeBase->errExpr ) return error4describe( p4->codeBase, e4unrecValue, E90910, (char *)p4->scan.ptr, 0, 0 ) ; return e4unrecValue ; } int s4stackPop( S4STACK *s4 ) { int retValue ; retValue = s4stackCur(s4) ; if ( s4->pos >= sizeof(int) ) s4->pos -= sizeof(int) ; return retValue ; } int s4stackCur( S4STACK *s4 ) { int pos, curData ; if ( s4->pos < sizeof(int) ) return E4NO_FUNCTION ; pos = s4->pos - sizeof(int) ; memcpy( (void *)&curData, s4->ptr+pos, sizeof(int) ) ; return curData ; } int s4stackPushInt( S4STACK *s4, const int i ) { return s4stackPushStr( s4, &i, sizeof(i)) ; } int s4stackPushStr( S4STACK *s4, const void *p, const int len ) { char *oldPtr ; if ( error4code( s4->codeBase ) < 0 ) return -1 ; if ( s4->pos+len > s4->len ) { oldPtr = s4->ptr ; if ( ! s4->doExtend ) s4->ptr = 0 ; else s4->ptr = (char *)u4allocFree( s4->codeBase, (long)s4->len + 256L ) ; if ( s4->ptr == 0 ) { s4->ptr = oldPtr ; if ( s4->codeBase->errExpr ) return error4( s4->codeBase, e4memory, E90911 ) ; return e4memory ; } memcpy( s4->ptr, oldPtr, s4->len ) ; u4free( oldPtr ) ; s4->len += 256 ; return s4stackPushStr( s4, p, len ) ; } else { memcpy( s4->ptr+s4->pos, p, (unsigned int)len ) ; s4->pos += len ; } return 0 ; } unsigned char s4scanChar( S4SCAN *s4 ) { if ( s4->pos >= s4->len ) return 0 ; return s4->ptr[s4->pos] ; } void s4scanInit( S4SCAN *s4, const unsigned char *p ) { s4->ptr = p ; s4->pos = 0 ; s4->len = strlen( (char *)p ) ; } int s4scanRange( S4SCAN *s4, const int startChar, const int endChar ) { int count ; for ( count = 0; s4->pos < s4->len; s4->pos++, count++ ) if ( s4->ptr[s4->pos] < startChar || s4->ptr[s4->pos] > endChar ) return count ; return count ; } int s4scanSearch( S4SCAN *s4, const char searchChar ) { int count ; for ( count = 0; s4->pos < s4->len; s4->pos++, count++ ) if ( s4->ptr[s4->pos] == searchChar ) return count ; return count ; } #ifdef S4VB_DOS EXPR4 *expr4parse_v( DATA4 *d4, char *expr ) { return expr4parseLow( d4, c4str(expr), 0 ) ; } #endif