1244 lines
32 KiB
C
1244 lines
32 KiB
C
|
/* e4parse.c (c)Copyright Sequiter Software Inc., 1990-1994. 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 3rd parameter
|
||
|
IIF must return a predictable length and type
|
||
|
TRIM and LTRIM returns an unpredictable length. Its result
|
||
|
can be operated on by the concatenate operator only.
|
||
|
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 <ctype.h>
|
||
|
|
||
|
void e4function_pop( 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 'code_base->expr_buf_len' 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 parm_pos, i_parm, is_ok, code_on, i_info, num_parms ;
|
||
|
int type_should_be, len, length_status, i, done_trim_memo_or_calc ;
|
||
|
int position[E4MAX_STACK_ENTRIES+1] ;
|
||
|
long length[E4MAX_STACK_ENTRIES] ;
|
||
|
long buf_len_needed ;
|
||
|
int types[E4MAX_STACK_ENTRIES] ;
|
||
|
int num_entries[E4MAX_STACK_ENTRIES] ;
|
||
|
E4INFO *pointers[E4MAX_STACK_ENTRIES] ;
|
||
|
CODE4 *code_base ;
|
||
|
unsigned stored_key_len ;
|
||
|
|
||
|
code_base = p4->code_base ;
|
||
|
num_parms = done_trim_memo_or_calc = 0 ;
|
||
|
buf_len_needed = 0 ;
|
||
|
|
||
|
position[0] = 0 ; /* The first parameter can be placed at position 0 */
|
||
|
|
||
|
for( i_info = 0; i_info < p4->expr.info_n; i_info++ )
|
||
|
{
|
||
|
info = p4->expr.info + i_info ;
|
||
|
|
||
|
/* Check the parameters types */
|
||
|
code_on = v4functions[info->function_i].code ;
|
||
|
if ( v4functions[info->function_i].num_parms != (char)info->num_parms )
|
||
|
if ( v4functions[info->function_i].num_parms > 0 )
|
||
|
{
|
||
|
if( code_base->expr_error )
|
||
|
e4( code_base, e4num_parms, p4->expr.source ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
for(;;)
|
||
|
{
|
||
|
if ( code_on != v4functions[info->function_i].code )
|
||
|
{
|
||
|
if( code_base->expr_error )
|
||
|
e4( code_base, e4type_sub, p4->expr.source ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
is_ok = 1 ;
|
||
|
|
||
|
for( i_parm = 0; i_parm < info->num_parms; i_parm++ )
|
||
|
{
|
||
|
if ( (int)v4functions[info->function_i].num_parms < 0 )
|
||
|
type_should_be = v4functions[info->function_i].type[0] ;
|
||
|
else
|
||
|
type_should_be = v4functions[info->function_i].type[i_parm] ;
|
||
|
|
||
|
parm_pos = num_parms - info->num_parms + i_parm ;
|
||
|
|
||
|
if ( types[parm_pos] != type_should_be )
|
||
|
{
|
||
|
if ( types[parm_pos] == r4date && type_should_be == r4date_doub )
|
||
|
{
|
||
|
pointers[parm_pos]->function_i = E4FIELD_DATE_D ;
|
||
|
length[parm_pos] = sizeof(double) ;
|
||
|
continue ;
|
||
|
}
|
||
|
if ( types[parm_pos] == r4num && type_should_be == r4num_doub )
|
||
|
{
|
||
|
pointers[parm_pos]->function_i = E4FIELD_NUM_D ;
|
||
|
length[parm_pos] = sizeof(double) ;
|
||
|
continue ;
|
||
|
}
|
||
|
|
||
|
info->function_i++ ;
|
||
|
is_ok = 0 ;
|
||
|
break ;
|
||
|
}
|
||
|
}
|
||
|
if ( is_ok )
|
||
|
break ;
|
||
|
}
|
||
|
|
||
|
switch( info->function_i )
|
||
|
{
|
||
|
case E4CONCATENATE:
|
||
|
case E4CONCAT_TWO:
|
||
|
case E4TRIM:
|
||
|
case E4LTRIM:
|
||
|
case E4UPPER:
|
||
|
case E4SUBSTR:
|
||
|
case E4LEFT:
|
||
|
#ifdef S4CLIPPER
|
||
|
case E4DESCEND_STR:
|
||
|
/* case E4DESCEND_STR+1: */
|
||
|
#endif
|
||
|
#ifndef S4MEMO_OFF
|
||
|
case E4FIELD_MEMO:
|
||
|
#endif
|
||
|
for( i_parm = 1; i_parm <= info->num_parms; i_parm++ )
|
||
|
{
|
||
|
E4INFO *info_parm = pointers[num_parms-i_parm] ;
|
||
|
if ( info_parm->function_i == E4FIELD_STR )
|
||
|
/* Make sure the parameter is put on the stack. */
|
||
|
info_parm->function_i = E4FIELD_STR_CAT ;
|
||
|
if ( info->function_i == E4CONCATENATE && done_trim_memo_or_calc )
|
||
|
info->function_i = E4CONCAT_TRIM ;
|
||
|
}
|
||
|
break ;
|
||
|
default:
|
||
|
break ;
|
||
|
}
|
||
|
|
||
|
num_parms -= info->num_parms ;
|
||
|
if ( num_parms < 0 )
|
||
|
{
|
||
|
if( code_base->expr_error )
|
||
|
e4( code_base, e4result, 0 ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
types[num_parms] = v4functions[info->function_i].return_type ;
|
||
|
|
||
|
if ( info->function_i == E4CALC_FUNCTION )
|
||
|
types[num_parms] = expr4type( ((EXPR4CALC *) info->p1)->expr ) ;
|
||
|
switch( types[num_parms] )
|
||
|
{
|
||
|
case r4str:
|
||
|
switch( info->function_i )
|
||
|
{
|
||
|
case E4FIELD_STR:
|
||
|
case E4FIELD_STR_CAT:
|
||
|
length[num_parms] = f4len( info->field_ptr ) ;
|
||
|
break ;
|
||
|
|
||
|
#ifndef S4MEMO_OFF
|
||
|
case E4FIELD_MEMO:
|
||
|
length[num_parms] = code_base->mem_size_memo_expr ;
|
||
|
done_trim_memo_or_calc = 1 ;
|
||
|
break ;
|
||
|
#endif /* S4MEMO_OFF */
|
||
|
|
||
|
case E4CONCATENATE:
|
||
|
case E4CONCAT_TWO:
|
||
|
case E4CONCAT_TRIM:
|
||
|
info->i1 = (int) (length[num_parms]) ;
|
||
|
length[num_parms] += length[num_parms+1] ;
|
||
|
break ;
|
||
|
|
||
|
case E4IIF:
|
||
|
if ( length[num_parms+1] != length[num_parms+2] )
|
||
|
{
|
||
|
if( code_base->expr_error )
|
||
|
e4describe( code_base, e4length_err, p4->expr.source, 0, 0 ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
length[num_parms] = length[num_parms+1] ;
|
||
|
break ;
|
||
|
|
||
|
case E4DTOS:
|
||
|
case E4DTOS+1:
|
||
|
length[num_parms] = 8 ;
|
||
|
break ;
|
||
|
|
||
|
case E4DTOC:
|
||
|
case E4DTOC+1:
|
||
|
case E4CTOD:
|
||
|
length[num_parms] = 8 ;
|
||
|
info->i1 = p4->constants.pos ;
|
||
|
len = strlen( code_base->date_format) ;
|
||
|
s4stack_push_str( &p4->constants, code_base->date_format,
|
||
|
len + 1 ) ;
|
||
|
if ( info->function_i == E4DTOC || info->function_i == E4DTOC+1 )
|
||
|
length[num_parms] = len ;
|
||
|
break ;
|
||
|
|
||
|
case E4DEL:
|
||
|
length[num_parms] = 1 ;
|
||
|
info->p1 = (char *)&p4->expr.data->record ;
|
||
|
break ;
|
||
|
|
||
|
case E4CALC_FUNCTION:
|
||
|
done_trim_memo_or_calc = 1 ;
|
||
|
length[num_parms] = expr4len( ((EXPR4CALC *) info->p1)->expr ) ;
|
||
|
break ;
|
||
|
|
||
|
case E4SUBSTR:
|
||
|
case E4LEFT:
|
||
|
if ( info->i1 > (int)(length[num_parms]) )
|
||
|
info->i1 = (int)(length[num_parms]) ;
|
||
|
if ( info->i1 < 0 )
|
||
|
info->i1 = 0 ;
|
||
|
length[num_parms] -= info->i1 ;
|
||
|
if ( info->len > (int)(length[num_parms]) )
|
||
|
info->len = (int)(length[num_parms]) ;
|
||
|
length[num_parms] = info->len ;
|
||
|
break ;
|
||
|
|
||
|
case E4TIME:
|
||
|
length[num_parms] = 8 ;
|
||
|
break ;
|
||
|
|
||
|
case E4TRIM:
|
||
|
case E4LTRIM:
|
||
|
done_trim_memo_or_calc = 1 ;
|
||
|
p4->expr.has_trim = 1 ;
|
||
|
break ;
|
||
|
|
||
|
case E4UPPER:
|
||
|
case E4DESCEND_STR:
|
||
|
/* case E4DESCEND_STR+1: */
|
||
|
break ;
|
||
|
|
||
|
default:
|
||
|
length[num_parms] = info->len ;
|
||
|
}
|
||
|
break ;
|
||
|
|
||
|
case r4num:
|
||
|
length[num_parms] = f4len( info->field_ptr ) ;
|
||
|
break ;
|
||
|
|
||
|
case r4num_doub:
|
||
|
case r4date_doub:
|
||
|
length[num_parms] = sizeof(double) ;
|
||
|
if ( info->function_i == E4CTOD )
|
||
|
{
|
||
|
info->i1 = p4->constants.pos ;
|
||
|
s4stack_push_str( &p4->constants, code_base->date_format,
|
||
|
strlen( code_base->date_format) + 1 ) ;
|
||
|
}
|
||
|
if ( info->function_i == E4RECCOUNT || info->function_i == E4RECNO )
|
||
|
info->p1 = (char *) p4->expr.data ;
|
||
|
break ;
|
||
|
|
||
|
case r4date:
|
||
|
length[num_parms] = 8 ;
|
||
|
break ;
|
||
|
|
||
|
case r4log:
|
||
|
if ( info->function_i != E4FIELD_LOG )
|
||
|
{
|
||
|
if ( info->function_i == E4DELETED )
|
||
|
info->p1 = (char *) &p4->expr.data->record ;
|
||
|
else
|
||
|
{
|
||
|
info->i1 = (int)(length[num_parms+1]) ;
|
||
|
length_status = 1 ;
|
||
|
if ( length[num_parms] < length[num_parms+1] )
|
||
|
{
|
||
|
info->i1 = (int)(length[num_parms]) ;
|
||
|
length_status = -1 ;
|
||
|
}
|
||
|
if ( length[num_parms] == length[num_parms+1] )
|
||
|
length_status = 0 ;
|
||
|
|
||
|
if ( info->function_i == E4GREATER )
|
||
|
{
|
||
|
if ( length_status > 0 )
|
||
|
info->p1 = (char *)1L ;
|
||
|
else
|
||
|
info->p1 = (char *)0 ;
|
||
|
}
|
||
|
if ( info->function_i == E4LESS )
|
||
|
{
|
||
|
if ( length_status < 0 )
|
||
|
info->p1 = (char *)1L ;
|
||
|
else
|
||
|
info->p1 = (char *)0 ;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
length[num_parms] = sizeof(int) ;
|
||
|
break ;
|
||
|
|
||
|
default:
|
||
|
#ifdef S4DEBUG
|
||
|
return e4( code_base, e4result, 0 ) ;
|
||
|
#else
|
||
|
return -1 ;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
/* make sure there is enough key space allocated for the type,
|
||
|
in case a partial evaluation occurs */
|
||
|
switch( types[num_parms] )
|
||
|
{
|
||
|
#ifdef S4FOX
|
||
|
case r4num:
|
||
|
case r4date:
|
||
|
case r4num_doub:
|
||
|
stored_key_len = sizeof( double ) ;
|
||
|
break ;
|
||
|
#endif /* ifdef S4FOX */
|
||
|
#ifdef S4CLIPPER
|
||
|
case r4num: /* numeric field return, must fix length problem */
|
||
|
stored_key_len = f4len( info->field_ptr ) ;
|
||
|
break ;
|
||
|
case r4num_doub:
|
||
|
stored_key_len = code_base->numeric_str_len ;
|
||
|
break ;
|
||
|
#endif /* ifdef S4CLIPPER */
|
||
|
#ifdef S4NDX
|
||
|
case r4num:
|
||
|
case r4date:
|
||
|
stored_key_len = sizeof( double ) ;
|
||
|
break ;
|
||
|
#endif /* ifdef S4NDX */
|
||
|
#ifdef S4MDX
|
||
|
case r4num:
|
||
|
stored_key_len = (int)sizeof( C4BCD ) ;
|
||
|
break ;
|
||
|
case r4num_doub:
|
||
|
stored_key_len = (int)sizeof( C4BCD ) ;
|
||
|
break ;
|
||
|
case r4date:
|
||
|
case r4date_doub:
|
||
|
stored_key_len = sizeof( double ) ;
|
||
|
break ;
|
||
|
#endif /* S4MDX */
|
||
|
default:
|
||
|
stored_key_len = (unsigned)(length[num_parms]) ;
|
||
|
}
|
||
|
|
||
|
u4alloc_again( code_base, &code_base->stored_key, &code_base->stored_key_len, stored_key_len + 1 ) ;
|
||
|
|
||
|
if ( code_base->error_code < 0 )
|
||
|
return -1 ;
|
||
|
|
||
|
info->result_pos = position[num_parms] ;
|
||
|
buf_len_needed = length[num_parms] ;
|
||
|
if ( info->function_i == E4CALC_FUNCTION )
|
||
|
buf_len_needed = ((EXPR4 *)info->p1)->len_eval ;
|
||
|
if( buf_len_needed > INT_MAX )
|
||
|
{
|
||
|
e4( code_base, e4overflow, 0 ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
if( (types[num_parms] == r4num || types[num_parms] == r4date) &&
|
||
|
length[num_parms] < sizeof(double) )
|
||
|
position[num_parms+1] = position[num_parms] + sizeof(double) ;
|
||
|
else
|
||
|
position[num_parms+1] = position[num_parms] + (unsigned)length[num_parms] ;
|
||
|
if ( position[num_parms] + buf_len_needed > p4->expr.len_eval )
|
||
|
p4->expr.len_eval = position[num_parms] + (unsigned)buf_len_needed ;
|
||
|
info->len = (int)(length[num_parms]) ;
|
||
|
|
||
|
info->num_entries = 1 ;
|
||
|
for( i = 0; i < info->num_parms; i++ )
|
||
|
info->num_entries += num_entries[num_parms+i] ;
|
||
|
|
||
|
num_entries[num_parms] = info->num_entries ;
|
||
|
pointers[num_parms] = info ;
|
||
|
|
||
|
num_parms++ ;
|
||
|
if ( num_parms >= E4MAX_STACK_ENTRIES )
|
||
|
{
|
||
|
if( code_base->expr_error )
|
||
|
e4( code_base, e4overflow, 0 ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( num_parms != 1 )
|
||
|
{
|
||
|
if( code_base->expr_error )
|
||
|
e4( code_base, e4result, 0 ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
for( i = 0; i < p4->expr.info_n; i++ )
|
||
|
{
|
||
|
info = p4->expr.info + i ;
|
||
|
info->function = (S4OPERATOR S4PTR *)v4functions[info->function_i].function_ptr ;
|
||
|
}
|
||
|
|
||
|
p4->expr.len_eval += 1 ;
|
||
|
if ( code_base->expr_buf_len < (unsigned)p4->expr.len_eval )
|
||
|
if ( u4alloc_again( code_base, &code_base->expr_work_buf, &code_base->expr_buf_len, p4->expr.len_eval ) == e4memory )
|
||
|
return -1 ;
|
||
|
|
||
|
p4->expr.len = (int)(length[0]) ;
|
||
|
p4->expr.type = types[0] ;
|
||
|
return 0 ;
|
||
|
}
|
||
|
|
||
|
int e4add_constant( E4PARSE *p4, int i_functions, void *cons_ptr, unsigned cons_len )
|
||
|
{
|
||
|
E4INFO *info ;
|
||
|
|
||
|
info = e4function_add( &p4->expr, i_functions ) ;
|
||
|
if ( info == 0 )
|
||
|
return -1 ;
|
||
|
info->i1 = p4->constants.pos ;
|
||
|
info->len = cons_len ;
|
||
|
return s4stack_push_str( &p4->constants, cons_ptr, (int) cons_len ) ;
|
||
|
}
|
||
|
|
||
|
E4INFO *e4function_add( EXPR4 *expr, int i_functions )
|
||
|
{
|
||
|
E4INFO *info ;
|
||
|
|
||
|
if ( (unsigned)((expr->info_n+1)*sizeof(E4INFO)) > expr->code_base->expr_buf_len )
|
||
|
if ( u4alloc_again( expr->code_base, &expr->code_base->expr_work_buf, &expr->code_base->expr_buf_len, sizeof(E4INFO) * (expr->info_n+10) ) == e4memory )
|
||
|
return 0 ;
|
||
|
|
||
|
info = (E4INFO *)expr->code_base->expr_work_buf + expr->info_n++ ;
|
||
|
|
||
|
info->function_i = i_functions ;
|
||
|
info->num_parms = v4functions[i_functions].num_parms ;
|
||
|
if ( info->num_parms < 0 )
|
||
|
info->num_parms = 2 ;
|
||
|
info->function = (S4OPERATOR S4PTR *)v4functions[i_functions].function_ptr ;
|
||
|
return info ;
|
||
|
}
|
||
|
|
||
|
void e4function_pop( EXPR4 *expr )
|
||
|
{
|
||
|
expr->info_n-- ;
|
||
|
}
|
||
|
|
||
|
EXPR4 *S4FUNCTION expr4parse( DATA4 *d4, char *expr_ptr )
|
||
|
{
|
||
|
E4PARSE parse ;
|
||
|
char ops[128] ;
|
||
|
char constants[512] ;
|
||
|
int rc, info_len, pos_constants ;
|
||
|
EXPR4 *express4 ;
|
||
|
|
||
|
#ifdef S4DEBUG
|
||
|
if ( d4 == 0 || expr_ptr == 0 )
|
||
|
e4severe( e4parm, E4_EXPR4PARSE ) ;
|
||
|
#endif
|
||
|
|
||
|
if ( d4->code_base->error_code < 0 )
|
||
|
return 0 ;
|
||
|
|
||
|
if ( d4->code_base->expr_buf_len > 0 )
|
||
|
memset( d4->code_base->expr_work_buf, 0, d4->code_base->expr_buf_len ) ;
|
||
|
|
||
|
memset( (void *)&parse, 0, sizeof(E4PARSE) ) ;
|
||
|
memset( ops, 0, sizeof(ops));
|
||
|
|
||
|
parse.expr.data = d4 ;
|
||
|
parse.expr.source = expr_ptr ;
|
||
|
parse.code_base = d4->code_base ;
|
||
|
parse.expr.code_base = d4->code_base ;
|
||
|
|
||
|
parse.op.ptr = ops ;
|
||
|
parse.op.len = sizeof(ops) ;
|
||
|
parse.op.code_base = d4->code_base ;
|
||
|
|
||
|
parse.constants.ptr = constants ;
|
||
|
parse.constants.len = sizeof(constants) ;
|
||
|
parse.constants.code_base = d4->code_base ;
|
||
|
|
||
|
s4scan_init( &parse.scan, expr_ptr ) ;
|
||
|
|
||
|
rc = expr4parse_expr( &parse ) ;
|
||
|
if ( rc < 0 )
|
||
|
return 0 ;
|
||
|
|
||
|
if ( s4stack_cur( &parse.op ) != E4NO_FUNCTION )
|
||
|
{
|
||
|
if( parse.code_base->expr_error )
|
||
|
e4( parse.code_base, e4complete, expr_ptr ) ;
|
||
|
return 0 ;
|
||
|
}
|
||
|
|
||
|
parse.expr.info = (E4INFO *)parse.code_base->expr_work_buf ;
|
||
|
if ( e4massage( &parse ) < 0 )
|
||
|
return 0 ;
|
||
|
|
||
|
info_len = parse.expr.info_n * sizeof(E4INFO) ;
|
||
|
pos_constants = sizeof(EXPR4) + info_len ;
|
||
|
|
||
|
express4 = (EXPR4 *)u4alloc_free( d4->code_base, pos_constants + parse.constants.len + parse.scan.len + 1 ) ;
|
||
|
if ( express4 == 0 )
|
||
|
return 0 ;
|
||
|
|
||
|
memcpy( (void *)express4, (void *)&parse.expr, sizeof(EXPR4) ) ;
|
||
|
|
||
|
express4->data = d4 ;
|
||
|
express4->info = (E4INFO *)( express4 + 1 ) ;
|
||
|
express4->constants = (char *) express4 + pos_constants ;
|
||
|
express4->source = express4->constants + parse.constants.len ;
|
||
|
|
||
|
memcpy( (void *)express4->info, parse.code_base->expr_work_buf, info_len ) ;
|
||
|
memcpy( express4->constants, constants, parse.constants.len ) ;
|
||
|
strcpy( express4->source, expr_ptr ) ;
|
||
|
|
||
|
#ifdef S4CLIPPER
|
||
|
express4->key_len = parse.expr.key_len ;
|
||
|
express4->key_dec = parse.expr.key_dec ;
|
||
|
#endif
|
||
|
|
||
|
return express4 ;
|
||
|
}
|
||
|
|
||
|
/* 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 e4get_operator( E4PARSE *p4, int *op_return)
|
||
|
{
|
||
|
char ch ;
|
||
|
int op ;
|
||
|
|
||
|
s4scan_range( &p4->scan, 1, ' ' ) ;
|
||
|
ch = s4scan_char(&p4->scan) ;
|
||
|
if ( ch==0 || ch==')' || ch==',')
|
||
|
{
|
||
|
*op_return = E4DONE ;
|
||
|
return(0) ; /* Done */
|
||
|
}
|
||
|
|
||
|
op = e4lookup( p4->scan.ptr+p4->scan.pos, -1, E4FIRST_OPERATOR, E4LAST_OPERATOR ) ;
|
||
|
if ( op < 0 )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4unrec_operator, p4->scan.ptr ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
p4->scan.pos += v4functions[op].name_len ;
|
||
|
*op_return = op ;
|
||
|
|
||
|
return 0 ;
|
||
|
}
|
||
|
|
||
|
/* e4lookup, searches 'v4functions' for an operator or function.
|
||
|
|
||
|
str - the function name
|
||
|
str_len - If 'str_len' 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( char *str, int str_len, int start_i, int end_i )
|
||
|
{
|
||
|
char u_str[9] ; /* Maximum # of function name characters plus one. */
|
||
|
int i ;
|
||
|
|
||
|
u4ncpy( u_str, str, sizeof(u_str) ) ;
|
||
|
c4upper( u_str ) ;
|
||
|
|
||
|
for( i=start_i; i<= end_i; i++)
|
||
|
{
|
||
|
if ( v4functions[i].code < 0 )
|
||
|
break ;
|
||
|
if ( v4functions[i].name == 0 )
|
||
|
continue ;
|
||
|
#ifdef S4DEBUG
|
||
|
if ( v4functions[i].name_len >= (char)sizeof(u_str) )
|
||
|
e4severe( e4result, E4_EXPR4LOOKUP ) ;
|
||
|
#endif
|
||
|
|
||
|
if ( v4functions[i].name[0] == u_str[0] )
|
||
|
if( str_len == v4functions[i].name_len || str_len < 0 )
|
||
|
if (strncmp(u_str, v4functions[i].name, (size_t) v4functions[i].name_len) == 0)
|
||
|
return( i ) ;
|
||
|
}
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
static int op_to_expr( E4PARSE *p4 )
|
||
|
{
|
||
|
E4INFO *info ;
|
||
|
|
||
|
info = e4function_add( &p4->expr, s4stack_pop(&p4->op) ) ;
|
||
|
if ( info == 0 )
|
||
|
return -1 ;
|
||
|
|
||
|
for(; s4stack_cur(&p4->op) == E4ANOTHER_PARM; )
|
||
|
{
|
||
|
s4stack_pop(&p4->op) ;
|
||
|
info->num_parms++ ;
|
||
|
}
|
||
|
|
||
|
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 expr4parse_expr( E4PARSE *p4 )
|
||
|
{
|
||
|
int op_value, op_on_stack ;
|
||
|
|
||
|
if ( expr4parse_value(p4) < 0 )
|
||
|
return -1 ;
|
||
|
|
||
|
for(;;)
|
||
|
{
|
||
|
if ( e4get_operator(p4, &op_value) < 0 )
|
||
|
return -1 ;
|
||
|
if ( op_value == E4DONE ) /* Done */
|
||
|
{
|
||
|
while( s4stack_cur(&p4->op) != E4L_BRACKET
|
||
|
&& s4stack_cur(&p4->op) != E4COMMA
|
||
|
&& s4stack_cur(&p4->op) != E4NO_FUNCTION )
|
||
|
if( op_to_expr( p4 ) < 0 )
|
||
|
return -1 ;
|
||
|
return( 0) ;
|
||
|
}
|
||
|
|
||
|
/* Everything with a higher or equal priority than 'op_value' must be
|
||
|
executed first. (equal because of left to right evaluation order)
|
||
|
Consequently, all high priority operators are sent to the result
|
||
|
stack.
|
||
|
*/
|
||
|
while ( s4stack_cur(&p4->op) >= 0 )
|
||
|
{
|
||
|
op_on_stack = s4stack_cur(&p4->op) ;
|
||
|
if ( v4functions[op_value].priority <=
|
||
|
v4functions[op_on_stack].priority)
|
||
|
{
|
||
|
if ( op_value == op_on_stack && (int)v4functions[op_value].num_parms < 0)
|
||
|
{
|
||
|
/* If repeated AND or OR operator, combine them into one with an
|
||
|
extra paramter. This makes the relate module optimization
|
||
|
algorithms easier. */
|
||
|
s4stack_pop(&p4->op) ;
|
||
|
s4stack_push_int( &p4->op, E4ANOTHER_PARM ) ;
|
||
|
break ;
|
||
|
}
|
||
|
else
|
||
|
if( op_to_expr( p4 ) < 0 )
|
||
|
return -1 ;
|
||
|
}
|
||
|
else
|
||
|
break ;
|
||
|
}
|
||
|
|
||
|
s4stack_push_int( &p4->op, op_value) ;
|
||
|
|
||
|
if ( expr4parse_value(p4) < 0 )
|
||
|
return -1 ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int expr4parse_function( E4PARSE *p4, char *start_ptr, int f_len )
|
||
|
{
|
||
|
int f_num, num_parms, info_i1, info_len ;
|
||
|
char ch ;
|
||
|
#ifdef S4UNIX
|
||
|
double doub_val ;
|
||
|
#endif
|
||
|
E4INFO *info ;
|
||
|
void *new_or_total_ptr = 0 ;
|
||
|
EXPR4CALC *calc ;
|
||
|
info_i1 = info_len = 0 ;
|
||
|
|
||
|
if ( p4->code_base->error_code < 0 )
|
||
|
return -1 ;
|
||
|
|
||
|
f_num = e4lookup( start_ptr, f_len, E4FIRST_FUNCTION, 0x7FFF) ;
|
||
|
if( f_num < 0 )
|
||
|
{
|
||
|
new_or_total_ptr = calc = expr4calc_lookup( p4->code_base, start_ptr, f_len ) ;
|
||
|
if( calc == 0 )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4unrec_function, p4->scan.ptr ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
f_num = E4CALC_FUNCTION ;
|
||
|
if( calc->total != 0 )
|
||
|
{
|
||
|
f_num = E4TOTAL ;
|
||
|
new_or_total_ptr = calc->total ;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
s4stack_push_int( &p4->op, E4L_BRACKET ) ;
|
||
|
p4->scan.pos++ ;
|
||
|
|
||
|
num_parms = 0 ;
|
||
|
for(;;)
|
||
|
{
|
||
|
ch = s4scan_char( &p4->scan ) ;
|
||
|
if ( ch == 0 )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4right_missing, p4->scan.ptr ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
if ( ch == ')')
|
||
|
{
|
||
|
p4->scan.pos++ ;
|
||
|
break ;
|
||
|
}
|
||
|
|
||
|
if ( expr4parse_expr(p4) < 0 )
|
||
|
return -1 ;
|
||
|
num_parms++ ;
|
||
|
|
||
|
while( s4scan_char( &p4->scan ) <= ' ' &&
|
||
|
s4scan_char( &p4->scan ) >='\1') p4->scan.pos++ ;
|
||
|
|
||
|
if ( s4scan_char( &p4->scan ) == ')')
|
||
|
{
|
||
|
p4->scan.pos++ ;
|
||
|
break ;
|
||
|
}
|
||
|
if ( s4scan_char( &p4->scan ) != ',')
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4comma_expected, p4->scan.ptr ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
p4->scan.pos++ ;
|
||
|
}
|
||
|
|
||
|
s4stack_pop( &p4->op ) ; /* pop the left bracket */
|
||
|
|
||
|
if ( f_num == E4STR )
|
||
|
{
|
||
|
info_len= 10 ;
|
||
|
|
||
|
if ( num_parms == 3 )
|
||
|
{
|
||
|
info = (E4INFO *) p4->code_base->expr_work_buf + p4->expr.info_n -1 ;
|
||
|
if ( info->function_i != E4DOUBLE )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4not_constant, p4->expr.source ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
#ifdef S4UNIX
|
||
|
memcpy( (void *)&doub_val, (p4->constants.ptr + info->i1), sizeof(double) ) ;
|
||
|
info_i1 = (int) doub_val ;
|
||
|
#else
|
||
|
info_i1 = (int) *(double *) (p4->constants.ptr + info->i1) ;
|
||
|
#endif
|
||
|
e4function_pop( &p4->expr ) ;
|
||
|
num_parms-- ;
|
||
|
}
|
||
|
if ( num_parms == 2 )
|
||
|
{
|
||
|
info = (E4INFO *)p4->code_base->expr_work_buf + p4->expr.info_n -1 ;
|
||
|
if ( info->function_i != E4DOUBLE )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4not_constant, p4->expr.source ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
#ifdef S4UNIX
|
||
|
memcpy( (void *)&doub_val, (p4->constants.ptr + info->i1), sizeof(double) ) ;
|
||
|
info_len = (int) doub_val ;
|
||
|
#else
|
||
|
info_len = (int) *(double *) (p4->constants.ptr + info->i1) ;
|
||
|
#endif
|
||
|
|
||
|
e4function_pop( &p4->expr ) ;
|
||
|
num_parms-- ;
|
||
|
}
|
||
|
if ( info_len < 0 )
|
||
|
info_len = 10 ;
|
||
|
if ( info_len <= info_i1+1 )
|
||
|
info_i1 = info_len - 2 ;
|
||
|
if ( info_i1 < 0 )
|
||
|
info_i1 = 0 ;
|
||
|
}
|
||
|
if ( num_parms == 3 && f_num == E4SUBSTR || num_parms == 2 && f_num == E4LEFT )
|
||
|
{
|
||
|
info = (E4INFO *)p4->code_base->expr_work_buf + p4->expr.info_n -1 ;
|
||
|
if ( info->function_i != E4DOUBLE )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4not_constant, p4->expr.source ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
#ifdef S4UNIX
|
||
|
memcpy( (void *)&doub_val, (p4->constants.ptr + info->i1), sizeof(double) ) ;
|
||
|
info_len = (int) doub_val ;
|
||
|
#else
|
||
|
info_len = (int) *(double *) (p4->constants.ptr + info->i1) ;
|
||
|
#endif
|
||
|
e4function_pop( &p4->expr ) ;
|
||
|
num_parms-- ;
|
||
|
}
|
||
|
if ( num_parms == 2 && f_num == E4SUBSTR )
|
||
|
{
|
||
|
info = (E4INFO *) p4->code_base->expr_work_buf + p4->expr.info_n -1 ;
|
||
|
if ( info->function_i != E4DOUBLE )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4not_constant, p4->expr.source ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
#ifdef S4UNIX
|
||
|
memcpy( (void *)&doub_val, (p4->constants.ptr + info->i1), sizeof(double) ) ;
|
||
|
info_i1 = (int) doub_val ;
|
||
|
#else
|
||
|
info_i1 = (int) *(double *) (p4->constants.ptr + info->i1) ;
|
||
|
#endif
|
||
|
info_i1-- ;
|
||
|
e4function_pop( &p4->expr ) ;
|
||
|
num_parms-- ;
|
||
|
}
|
||
|
|
||
|
if ( p4->code_base->error_code < 0 )
|
||
|
return -1 ;
|
||
|
|
||
|
if ( num_parms != v4functions[f_num].num_parms &&
|
||
|
(int)v4functions[f_num].num_parms >= 0 )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4describe( p4->code_base, e4num_parms, p4->scan.ptr, E4_NUM_PARMS, v4functions[f_num].name ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
info = e4function_add( &p4->expr, f_num ) ;
|
||
|
if ( info == 0 )
|
||
|
return -1 ;
|
||
|
|
||
|
info->i1 = info_i1 ;
|
||
|
info->len = info_len ;
|
||
|
|
||
|
info->num_parms = num_parms ;
|
||
|
if ( f_num == E4CALC_FUNCTION || f_num == E4TOTAL )
|
||
|
info->p1 = (char *) new_or_total_ptr ;
|
||
|
return 0 ;
|
||
|
}
|
||
|
|
||
|
int expr4parse_value( E4PARSE *p4 )
|
||
|
{
|
||
|
FIELD4 * field_ptr ;
|
||
|
char ch, *start_ptr, search_char ;
|
||
|
int i_functions, len, i_function, save_pos ;
|
||
|
double d ;
|
||
|
E4INFO *expr, *info ;
|
||
|
DATA4 *base_ptr ;
|
||
|
char b_name[11], f_name[11] ;
|
||
|
|
||
|
if ( p4->code_base->error_code < 0 )
|
||
|
return -1 ;
|
||
|
|
||
|
s4scan_range( &p4->scan, ' ', ' ' ) ;
|
||
|
|
||
|
/* expression */
|
||
|
|
||
|
if ( s4scan_char( &p4->scan ) == '(')
|
||
|
{
|
||
|
p4->scan.pos++ ;
|
||
|
|
||
|
s4stack_push_int( &p4->op, E4L_BRACKET) ;
|
||
|
if ( expr4parse_expr(p4) < 0 ) return( -1 ) ;
|
||
|
|
||
|
while ( s4scan_char( &p4->scan ) <= ' ' &&
|
||
|
s4scan_char( &p4->scan ) != 0) p4->scan.pos++ ;
|
||
|
|
||
|
if ( s4scan_char( &p4->scan ) != ')' )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4right_missing, p4->scan.ptr ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
p4->scan.pos++ ;
|
||
|
s4stack_pop( &p4->op ) ;
|
||
|
return( 0 ) ;
|
||
|
}
|
||
|
|
||
|
/* logical */
|
||
|
if ( s4scan_char( &p4->scan ) == '.' )
|
||
|
{
|
||
|
i_functions = e4lookup( p4->scan.ptr+p4->scan.pos, -1, E4FIRST_LOG, E4LAST_LOG ) ;
|
||
|
if ( i_functions >= 0 )
|
||
|
{
|
||
|
p4->scan.pos += v4functions[i_functions].name_len ;
|
||
|
|
||
|
if ( strcmp( v4functions[i_functions].name, ".NOT." ) == 0 )
|
||
|
{
|
||
|
if ( expr4parse_value(p4) < 0 ) return( -1 ) ; /* One operand operation */
|
||
|
s4stack_push_int( &p4->op, i_functions ) ;
|
||
|
return 0 ;
|
||
|
}
|
||
|
|
||
|
expr = e4function_add( &p4->expr, i_functions ) ;
|
||
|
if ( expr == 0 )
|
||
|
return -1 ;
|
||
|
return 0 ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* string */
|
||
|
ch = s4scan_char( &p4->scan ) ;
|
||
|
if ( ch == '\'' || ch == '\"' || ch == '[' )
|
||
|
{
|
||
|
if ( ch == '[' )
|
||
|
search_char = ']' ;
|
||
|
else
|
||
|
search_char = ch ;
|
||
|
|
||
|
p4->scan.pos++ ;
|
||
|
start_ptr = p4->scan.ptr + p4->scan.pos ;
|
||
|
|
||
|
len = s4scan_search( &p4->scan, search_char ) ;
|
||
|
if ( s4scan_char( &p4->scan ) != search_char )
|
||
|
if ( len < 0 )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4unterminated, p4->scan.ptr ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
p4->scan.pos++ ;
|
||
|
|
||
|
if ( e4add_constant( p4, E4STRING, start_ptr, len ) < 0 )
|
||
|
return -1 ;
|
||
|
return 0 ;
|
||
|
}
|
||
|
|
||
|
/* real */
|
||
|
ch = s4scan_char( &p4->scan ) ;
|
||
|
if ( ch >='0' && ch <='9' || ch == '-' || ch == '+' || ch == '.' )
|
||
|
{
|
||
|
start_ptr = p4->scan.ptr+p4->scan.pos ;
|
||
|
save_pos = p4->scan.pos ;
|
||
|
p4->scan.pos++ ;
|
||
|
len = 1 ;
|
||
|
|
||
|
while( s4scan_char( &p4->scan ) >= '0' &&
|
||
|
s4scan_char( &p4->scan ) <= '9' ||
|
||
|
s4scan_char( &p4->scan ) == '.' )
|
||
|
{
|
||
|
if ( s4scan_char( &p4->scan ) == '.' )
|
||
|
{
|
||
|
if ( strnicmp( p4->scan.ptr+p4->scan.pos, ".AND.",5) == 0 ||
|
||
|
strnicmp( p4->scan.ptr+p4->scan.pos, ".OR.",4) == 0 ||
|
||
|
strnicmp( 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( s4scan_char( &p4->scan + 1 ) ) >= 'A' && toupper( s4scan_char( &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( s4scan_char( &p4->scan ) ) >= 'A' && toupper( s4scan_char( &p4->scan ) ) <= 'Z' )
|
||
|
p4->scan.pos = save_pos ;
|
||
|
else
|
||
|
{
|
||
|
d = c4atod( start_ptr, len ) ;
|
||
|
if ( e4add_constant( p4, E4DOUBLE, &d, sizeof(d) ) < 0 )
|
||
|
return -1 ;
|
||
|
return 0 ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* function or field */
|
||
|
if (u4name_char(s4scan_char( &p4->scan )) )
|
||
|
{
|
||
|
start_ptr = p4->scan.ptr + p4->scan.pos ;
|
||
|
|
||
|
for( len=0; u4name_char(s4scan_char( &p4->scan )); len++ )
|
||
|
p4->scan.pos++ ;
|
||
|
|
||
|
s4scan_range( &p4->scan, (char)0, ' ' ) ;
|
||
|
|
||
|
if ( s4scan_char( &p4->scan ) == '(' )
|
||
|
return expr4parse_function( p4, start_ptr, len ) ;
|
||
|
|
||
|
base_ptr = 0 ;
|
||
|
|
||
|
#ifdef S4FOX
|
||
|
if ( s4scan_char( &p4->scan ) == '.' )
|
||
|
{ /* for fox, same as -> */
|
||
|
if ( len > 10 )
|
||
|
len = 10 ;
|
||
|
memmove( b_name, start_ptr, (size_t)len ) ;
|
||
|
b_name[len] = '\0' ;
|
||
|
|
||
|
base_ptr = d4data( p4->code_base, b_name) ;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ( s4scan_char( &p4->scan ) == '-' )
|
||
|
if ( p4->scan.ptr[p4->scan.pos+1] == '>')
|
||
|
{
|
||
|
if ( len > 10 )
|
||
|
len = 10 ;
|
||
|
memmove( b_name, start_ptr, (size_t)len ) ;
|
||
|
b_name[len] = '\0' ;
|
||
|
|
||
|
base_ptr = d4data( p4->code_base, b_name) ;
|
||
|
|
||
|
if ( base_ptr == 0 )
|
||
|
{
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4describe( p4->code_base, e4data_name, b_name, p4->scan.ptr, (char *) 0 ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
p4->scan.pos++ ;
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( base_ptr != 0 )
|
||
|
{
|
||
|
p4->scan.pos++ ;
|
||
|
|
||
|
start_ptr = p4->scan.ptr + p4->scan.pos ;
|
||
|
for( len=0; u4name_char(s4scan_char( &p4->scan )); len++ )
|
||
|
p4->scan.pos++ ;
|
||
|
}
|
||
|
else
|
||
|
base_ptr = (DATA4 *) p4->expr.data ;
|
||
|
|
||
|
if ( len <= 10)
|
||
|
{
|
||
|
memmove( f_name, start_ptr, (size_t) len ) ;
|
||
|
f_name[len] = 0 ;
|
||
|
field_ptr = d4field( base_ptr, f_name ) ;
|
||
|
if ( field_ptr == 0 )
|
||
|
return -1 ;
|
||
|
|
||
|
#ifdef S4CLIPPER
|
||
|
p4->expr.key_len = field_ptr->len ;
|
||
|
p4->expr.key_dec = field_ptr->dec ;
|
||
|
#endif
|
||
|
|
||
|
i_function = 0 ;
|
||
|
switch( field_ptr->type )
|
||
|
{
|
||
|
case 'N':
|
||
|
case 'F':
|
||
|
i_function = E4FIELD_NUM_S ;
|
||
|
break ;
|
||
|
case 'C':
|
||
|
i_function = E4FIELD_STR ;
|
||
|
break ;
|
||
|
case 'D':
|
||
|
i_function = E4FIELD_DATE_S ;
|
||
|
break ;
|
||
|
case 'L':
|
||
|
i_function = E4FIELD_LOG ;
|
||
|
break ;
|
||
|
case 'M':
|
||
|
#ifdef S4MEMO_OFF
|
||
|
return e4( p4->code_base, e4not_memo, E4_EXPR4PV ) ;
|
||
|
#else
|
||
|
i_function = E4FIELD_MEMO ;
|
||
|
break ;
|
||
|
#endif
|
||
|
default:
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4type_sub, E4_TYPE_UFT ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
info = e4function_add( &p4->expr, i_function ) ;
|
||
|
if ( info == 0 )
|
||
|
return -1 ;
|
||
|
info->field_ptr = field_ptr ;
|
||
|
info->p1 = (char *) &base_ptr->record ;
|
||
|
info->i1 = field_ptr->offset ;
|
||
|
|
||
|
return 0 ;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( p4->code_base->expr_error )
|
||
|
e4( p4->code_base, e4unrec_value, p4->scan.ptr ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
|
||
|
int s4stack_pop( S4STACK *s4 )
|
||
|
{
|
||
|
int ret_value ;
|
||
|
|
||
|
ret_value = s4stack_cur(s4) ;
|
||
|
|
||
|
if ( s4->pos >= sizeof(int) )
|
||
|
s4->pos -= sizeof(int) ;
|
||
|
return ret_value ;
|
||
|
}
|
||
|
|
||
|
int s4stack_cur( S4STACK *s4 )
|
||
|
{
|
||
|
int pos, cur_data ;
|
||
|
|
||
|
if ( s4->pos < sizeof(int) )
|
||
|
return E4NO_FUNCTION ;
|
||
|
pos = s4->pos - sizeof(int) ;
|
||
|
memcpy( (void *)&cur_data, s4->ptr+pos, sizeof(int) ) ;
|
||
|
return cur_data ;
|
||
|
}
|
||
|
|
||
|
int s4stack_push_int( S4STACK *s4, int i )
|
||
|
{
|
||
|
return s4stack_push_str( s4, &i, sizeof(i)) ;
|
||
|
}
|
||
|
|
||
|
int s4stack_push_str( S4STACK *s4, void *p, int len )
|
||
|
{
|
||
|
char *old_ptr ;
|
||
|
|
||
|
if ( s4->code_base->error_code < 0 )
|
||
|
return -1 ;
|
||
|
|
||
|
if ( s4->pos+len > s4->len )
|
||
|
{
|
||
|
old_ptr = s4->ptr ;
|
||
|
if ( ! s4->do_extend )
|
||
|
s4->ptr = 0 ;
|
||
|
else
|
||
|
s4->ptr = (char *)u4alloc_free( s4->code_base, s4->len + 256 ) ;
|
||
|
if ( s4->ptr == 0 )
|
||
|
{
|
||
|
s4->ptr = old_ptr ;
|
||
|
if( s4->code_base->expr_error )
|
||
|
e4( s4->code_base, e4memory, 0 ) ;
|
||
|
return -1 ;
|
||
|
}
|
||
|
memcpy( s4->ptr, old_ptr, s4->len ) ;
|
||
|
u4free( old_ptr ) ;
|
||
|
s4->len += 256 ;
|
||
|
|
||
|
return s4stack_push_str( s4, p, len ) ;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
memcpy( s4->ptr+s4->pos, p, len ) ;
|
||
|
s4->pos += len ;
|
||
|
}
|
||
|
return 0 ;
|
||
|
}
|
||
|
|
||
|
|
||
|
char s4scan_char( S4SCAN *s4 )
|
||
|
{
|
||
|
if ( s4->pos >= s4->len )
|
||
|
return 0 ;
|
||
|
return s4->ptr[s4->pos] ;
|
||
|
}
|
||
|
|
||
|
void s4scan_init( S4SCAN *s4, char *p )
|
||
|
{
|
||
|
s4->ptr = p ;
|
||
|
s4->pos = 0 ;
|
||
|
s4->len = strlen(p) ;
|
||
|
}
|
||
|
|
||
|
int s4scan_range( S4SCAN *s4, int start_char, int end_char )
|
||
|
{
|
||
|
int count ;
|
||
|
|
||
|
for ( count = 0; s4->pos < s4->len; s4->pos++, count++ )
|
||
|
if ( s4->ptr[s4->pos] < start_char || s4->ptr[s4->pos] > end_char )
|
||
|
return count ;
|
||
|
return count ;
|
||
|
}
|
||
|
|
||
|
int s4scan_search( S4SCAN *s4, char search_char )
|
||
|
{
|
||
|
int count ;
|
||
|
|
||
|
for ( count = 0; s4->pos < s4->len; s4->pos++, count++ )
|
||
|
if ( s4->ptr[s4->pos] == search_char )
|
||
|
return count ;
|
||
|
return count ;
|
||
|
}
|
||
|
|
||
|
#ifdef S4VB_DOS
|
||
|
|
||
|
EXPR4 *expr4parse_v( DATA4 *d4, char *expr )
|
||
|
{
|
||
|
return expr4parse( d4, c4str(expr) ) ;
|
||
|
}
|
||
|
|
||
|
#endif
|