/* * PROGRAM: JRD Command Oriented Query Language * MODULE: eval.c * DESCRIPTION: Value and boolean expression evaluator * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. */ #include "firebird.h" #include "../jrd/ib_stdio.h" #include #include "../jrd/gds.h" #include "../jrd/common.h" #include "../qli/dtr.h" #include "../qli/exe.h" #include "../qli/err_proto.h" #include "../qli/eval_proto.h" #include "../qli/exe_proto.h" #include "../qli/form_proto.h" #include "../qli/lex_proto.h" #include "../qli/mov_proto.h" #include "../qli/picst_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/utl_proto.h" typedef vary VARY; extern USHORT QLI_prompt_count, QLI_reprompt; extern USHORT sw_forms; static SLONG execute_any(QLI_NOD); static DSC *execute_concatenate(QLI_NOD, DSC *, DSC *); static DSC *execute_edit(QLI_NOD); static DSC *execute_function(QLI_NOD); static DSC *execute_prompt(QLI_NOD); static DSC *execute_statistical(QLI_NOD); static int like(UCHAR *, SSHORT, UCHAR *, SSHORT, UCHAR); static TEXT *make_blob_buffer(FRBRD *, USHORT *); static int matches(TEXT *, SSHORT, TEXT *, SSHORT); static int sleuth(QLI_NOD, DSC *, DSC *, DSC *); static int sleuth_check(USHORT, UCHAR *, UCHAR *, UCHAR *, UCHAR *); static int sleuth_class(USHORT, UCHAR *, UCHAR *, UCHAR); static int sleuth_merge(UCHAR *, UCHAR *, UCHAR *, UCHAR *, UCHAR *); static int string_boolean(QLI_NOD); static int string_function(QLI_NOD, SSHORT, TEXT *, SSHORT, TEXT *); #define TEMP_LENGTH 128 #define SLEUTH_insensitive 1 #define COND_UPPER(c) ((flags & SLEUTH_insensitive) ? UPPER(c) : c) static UCHAR special[127] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; static TEXT prompt[2][128] = { "", "" }; int EVAL_boolean( QLI_NOD node) { /************************************** * * E V A L _ b o o l e a n * ************************************** * * Functional description * Evaluate a boolean expression. * **************************************/ DSC *value1, *value2; int result; switch (node->nod_type) { case nod_eql: case nod_neq: case nod_gtr: case nod_geq: case nod_leq: case nod_lss: case nod_between: if (!(value1 = EVAL_value(node->nod_arg[0])) || (value1->dsc_missing & DSC_missing) || !(value2 = EVAL_value(node->nod_arg[1])) || (value2->dsc_missing & DSC_missing)) return FALSE; if (node->nod_flags & nod_comparison) result = MOVQ_compare(value1, value2); break; case nod_and: case nod_or: case nod_not: result = EVAL_boolean(node->nod_arg[0]); break; case nod_containing: case nod_matches: case nod_like: case nod_starts: case nod_sleuth: result = string_boolean(node); } switch (node->nod_type) { case nod_eql: return (result == 0); case nod_neq: return (result != 0); case nod_gtr: return (result > 0); case nod_geq: return (result >= 0); case nod_leq: return (result <= 0); case nod_lss: return (result < 0); case nod_between: if (result < 0) return FALSE; if (!(value2 = EVAL_value(node->nod_arg[2])) || (value2->dsc_missing & DSC_missing)) return FALSE; result = MOVQ_compare(value1, value2); if (result > 0) return FALSE; return TRUE; case nod_missing: value1 = EVAL_value(node->nod_arg[0]); if (value1) return (value1->dsc_missing & DSC_missing); return TRUE; case nod_and: if (!result) return FALSE; return EVAL_boolean(node->nod_arg[1]); case nod_or: if (result) return TRUE; return EVAL_boolean(node->nod_arg[1]); case nod_not: return !result; case nod_any: case nod_unique: return execute_any(node); case nod_sleuth: case nod_containing: case nod_matches: case nod_like: case nod_starts: return result; default: BUGCHECK(28); /* Msg28 EVAL_boolean: not finished */ return NULL; } } void EVAL_break_compute( QLI_NOD node) { /************************************** * * E V A L _ b r e a k _ c o m p u t e * ************************************** * * Functional description * Compute the value of a statistical expression at control * break. The only function that needs any work is average. * **************************************/ if (node->nod_type == nod_rpt_average && node->nod_arg[e_stt_default]) if (node->nod_desc.dsc_dtype == dtype_long) *(SLONG *) node->nod_desc.dsc_address /= (SLONG) node-> nod_arg[e_stt_default]; else *(double *) node->nod_desc.dsc_address /= (SLONG) node-> nod_arg[e_stt_default]; } void EVAL_break_increment( QLI_NOD node) { /************************************** * * E V A L _ b r e a k _ i n c r e m e n t * ************************************** * * Functional description * Initialize a report-local statistical function. * **************************************/ DSC *desc1, *desc2; SLONG count; SSHORT comparison; desc1 = &node->nod_desc; /* Knock off count as trivial */ if (node->nod_type == nod_rpt_count) { *(SLONG *) node->nod_desc.dsc_address += 1; return; } /* Evaluate the sub-expression. If null, don't bother to do anything more. If not, bump the number of records involved */ if (!(desc2 = EVAL_value(node->nod_arg[e_stt_value]))) return; /* If this is the first value, just move it in. */ count = (SLONG) node->nod_arg[e_stt_default] + 1; if (count == 1) { if (desc2->dsc_missing) desc1->dsc_missing = DSC_missing; else { desc1->dsc_missing = FALSE; MOVQ_move(desc2, desc1); node->nod_arg[e_stt_default] = (QLI_NOD) (SLONG) count; } return; } else if (desc2->dsc_missing) return; node->nod_arg[e_stt_default] = (QLI_NOD) (SLONG) count; desc1->dsc_missing = FALSE; /* Finish off as per operator */ switch (node->nod_type) { case nod_rpt_min: case nod_rpt_max: if (!(comparison = MOVQ_compare(desc2, desc1))) break; if ((comparison > 0 && node->nod_type == nod_rpt_max) || (comparison < 0 && node->nod_type == nod_rpt_min)) MOVQ_move(desc2, desc1); break; case nod_rpt_total: case nod_rpt_average: if (desc1->dsc_dtype == dtype_long) *(SLONG *) desc1->dsc_address += MOVQ_get_long(desc2, desc1->dsc_scale); else *(double *) desc1->dsc_address += MOVQ_get_double(desc2); break; } } void EVAL_break_init( QLI_NOD node) { /************************************** * * E V A L _ b r e a k _ i n i t * ************************************** * * Functional description * Initialize a report-local statistical function. * **************************************/ node->nod_arg[e_stt_default] = 0; node->nod_desc.dsc_missing = FALSE; if (node->nod_type == nod_rpt_count) *(SLONG *) (node->nod_desc.dsc_address) = 0; } DSC *EVAL_parameter(PAR parameter) { /************************************** * * E V A L _ p a r a m e t e r * ************************************** * * Functional description * Compute the descriptor for a parameter. * **************************************/ DSC *desc; QLI_MSG message; PAR missing_parameter; USHORT *missing_flag; desc = ¶meter->par_desc; desc->dsc_missing = FALSE; message = parameter->par_message; if (missing_parameter = parameter->par_missing) { missing_flag = (USHORT *) (message->msg_buffer + missing_parameter->par_offset); desc->dsc_missing = (*missing_flag) ? DSC_missing : 0; } desc->dsc_address = message->msg_buffer + parameter->par_offset; return desc; } DSC *EVAL_value(QLI_NOD node) { /************************************** * * E V A L _ v a l u e * ************************************** * * Functional description * Evaluate a value node. * **************************************/ QLI_NOD *ptr, *end_ptr; QLI_FLD field; DSC *values[4], **value, *desc, *desc2; UCHAR *p; double d1; /* Start by evaluating sub-expressions (where appropriate) */ desc = &node->nod_desc; ptr = node->nod_arg; end_ptr = ptr + node->nod_count; for (value = values; ptr < end_ptr; ptr++, value++) { *value = EVAL_value(*ptr); if (node->nod_flags & nod_partial) break; } switch (node->nod_type) { case nod_edit_blob: return execute_edit(node); case nod_rpt_max: case nod_rpt_min: case nod_rpt_total: case nod_rpt_average: if (!(SLONG) node->nod_arg[e_stt_default]) desc->dsc_missing = DSC_missing; case nod_rpt_count: case nod_constant: return desc; case nod_variable: field = (QLI_FLD) node->nod_arg[e_fld_field]; desc->dsc_missing = (field->fld_flags & FLD_missing) ? DSC_missing : 0; return desc; case nod_field: return EVAL_value(node->nod_arg[e_fld_reference]); case nod_reference: return EVAL_parameter(node->nod_import); case nod_null: return desc; case nod_add: if ((values[0]->dsc_missing & DSC_missing) || (values[1]->dsc_missing & DSC_missing)) { desc->dsc_missing = DSC_missing; return desc; } desc->dsc_missing = FALSE; if (node->nod_flags & nod_date) { d1 = MOVQ_date_to_double(values[0]) + MOVQ_get_double(values[1]); MOVQ_double_to_date(d1, (long*) desc->dsc_address); } else if (desc->dsc_dtype == dtype_long) *((SLONG *) desc->dsc_address) = MOVQ_get_long(values[0], desc->dsc_scale) + MOVQ_get_long(values[1], desc->dsc_scale); else *((double *) desc->dsc_address) = MOVQ_get_double(values[0]) + MOVQ_get_double(values[1]); return desc; case nod_subtract: if ((values[0]->dsc_missing & DSC_missing) || (values[1]->dsc_missing & DSC_missing)) { desc->dsc_missing = DSC_missing; return desc; } desc->dsc_missing = FALSE; if (node->nod_flags & nod_date) { *((double *) desc->dsc_address) = MOVQ_date_to_double(values[0]) - MOVQ_date_to_double(values[1]); } else if (desc->dsc_dtype == dtype_long) *((SLONG *) desc->dsc_address) = MOVQ_get_long(values[0], desc->dsc_scale) - MOVQ_get_long(values[1], desc->dsc_scale); else *((double *) desc->dsc_address) = MOVQ_get_double(values[0]) - MOVQ_get_double(values[1]); return desc; case nod_divide: if ((values[0]->dsc_missing & DSC_missing) || (values[1]->dsc_missing & DSC_missing)) { desc->dsc_missing = DSC_missing; return desc; } desc->dsc_missing = FALSE; *((double *) desc->dsc_address) = MOVQ_get_double(values[0]) / MOVQ_get_double(values[1]); return desc; case nod_multiply: if ((values[0]->dsc_missing & DSC_missing) || (values[1]->dsc_missing & DSC_missing)) { desc->dsc_missing = DSC_missing; return desc; } desc->dsc_missing = FALSE; if (desc->dsc_dtype == dtype_long) *((SLONG *) desc->dsc_address) = MOVQ_get_long(values[0], values[0]->dsc_scale) * MOVQ_get_long(values[1], values[1]->dsc_scale); else *((double *) desc->dsc_address) = MOVQ_get_double(values[0]) * MOVQ_get_double(values[1]); return desc; case nod_negate: if (values[0]->dsc_missing & DSC_missing) { desc->dsc_missing = DSC_missing; return desc; } desc->dsc_missing = FALSE; switch (desc->dsc_dtype) { case dtype_short: *((SSHORT *) desc->dsc_address) = -MOVQ_get_long(values[0], desc->dsc_scale); break; case dtype_long: *((SLONG *) desc->dsc_address) = -MOVQ_get_long(values[0], desc->dsc_scale); break; case dtype_real: *((float *) desc->dsc_address) = -MOVQ_get_double(values[0]); break; case dtype_double: *((double *) desc->dsc_address) = -MOVQ_get_double(values[0]); break; default: IBERROR(30); /* Msg30 data type not supported for arithmetic */ } return desc; case nod_prompt: if (!prompt[0][0]) { ERRQ_msg_get(499, prompt[0]); /* Msg499 Re-enter */ ERRQ_msg_get(500, prompt[1]); /* Msg500 Enter */ } return execute_prompt(node); case nod_concatenate: return execute_concatenate(node, values[0], values[1]); case nod_function: return execute_function(node); case nod_max: case nod_min: case nod_count: case nod_average: case nod_total: case nod_from: return execute_statistical(node); case nod_running_count: *(SLONG *) (desc->dsc_address) += 1; return desc; case nod_running_total: if (desc2 = EVAL_value(node->nod_arg[e_stt_value])) { if (desc2->dsc_missing & DSC_missing) return desc; if (desc->dsc_dtype == dtype_long) *(SLONG *) desc->dsc_address += MOVQ_get_long(desc2, desc->dsc_scale); else *(double *) desc->dsc_address += MOVQ_get_double(desc2); } return desc; case nod_format: p = desc->dsc_address; PIC_edit(values[0], (pic*) node->nod_arg[e_fmt_picture], (TEXT**) &p, desc->dsc_length); desc->dsc_length = p - desc->dsc_address; return desc; case nod_form_field: return FORM_get_field(node); case nod_user_name: IBERROR(31); /* Msg31 user name is supported only in RSEs temporarily */ case nod_parameter: case nod_position: case nod_substr: case nod_via: default: BUGCHECK(29); /* Msg29 EVAL_value: not finished */ return NULL; } } static SLONG execute_any( QLI_NOD node) { /************************************** * * e x e c u t e _ a n y * ************************************** * * Functional description * Execute an ANY expression. This may require that a request get * started, a message sent, and a message received for each * record. At the other end of the spectrum, there may be * absolutely nothing to do. * **************************************/ QLI_REQ request; QLI_MSG message; /* If there is a request associated with the node, start it and possibly send a message along with it. */ if (request = (QLI_REQ) node->nod_arg[e_any_request]) EXEC_start_request(request, (QLI_MSG) node->nod_arg[e_any_send]); else if (message = (QLI_MSG) node->nod_arg[e_any_send]) EXEC_send(message); message = (QLI_MSG) node->nod_arg[e_any_receive]; EXEC_receive(message, 0); return MOVQ_get_long(EVAL_parameter(node->nod_import), 0); } static DSC *execute_concatenate( QLI_NOD node, DSC * value1, DSC * value2) { /************************************** * * e x e c u t e _ c o n c a t e n a t e * ************************************** * * Functional description * Concatenate two strings. * **************************************/ DSC *desc; VARY *vary; TEXT *p, temp1[32], temp2[32], *address1, *address2; USHORT length1, length2; length1 = MOVQ_get_string(value1, &address1, (VARY*)temp1, sizeof(temp1)); length2 = MOVQ_get_string(value2, &address2, (VARY*)temp2, sizeof(temp2)); desc = &node->nod_desc; vary = (VARY *) desc->dsc_address; p = vary->vary_string; length1 = MIN(length1, desc->dsc_length - 2); length2 = MAX(MIN(length2, desc->dsc_length - 2 - length1), 0); if (length1) do *p++ = *address1++; while (--length1); if (length2) do *p++ = *address2++; while (--length2); vary->vary_length = p - vary->vary_string; return desc; } static DSC *execute_edit( QLI_NOD node) { /************************************** * * e x e c u t e _ e d i t * ************************************** * * Functional description * Edit a blob. If there's an existing * blob for input, force the descriptor * to quad to get move the id. * **************************************/ SLONG *id; DSC *desc; DBB dbb; TEXT *field_name; dbb = (DBB) node->nod_arg[e_edt_dbb]; id = (SLONG *) & node->nod_arg[e_edt_id1]; desc = NULL; if (node->nod_arg[e_edt_input]) { desc = EVAL_value(node->nod_arg[e_edt_input]); if (desc && (desc->dsc_dtype == dtype_blob)) { desc->dsc_dtype = dtype_quad; MOVQ_move(desc, &node->nod_desc); desc->dsc_dtype = dtype_blob; } } if (!desc) id[0] = id[1] = NULL; field_name = (TEXT *) node->nod_arg[e_edt_name]; BLOB_edit((GDS_QUAD_t*)id, dbb->dbb_handle, dbb->dbb_transaction, field_name); node->nod_desc.dsc_missing = (id[0] || id[1]) ? 0 : DSC_missing; return &node->nod_desc; } static DSC *execute_function( QLI_NOD node) { /************************************** * * e x e c u t e _ f u n c t i o n * ************************************** * * Functional description * Execute a statistical expression. * **************************************/ QLI_REQ request; QLI_MSG message; /* If there is a request associated with the node, start it and possibly send a message along with it. */ if (request = (QLI_REQ) node->nod_arg[e_fun_request]) EXEC_start_request(request, (QLI_MSG) node->nod_arg[e_fun_send]); else if (message = (QLI_MSG) node->nod_arg[e_fun_send]) EXEC_send(message); return EXEC_receive((QLI_MSG) node->nod_arg[e_fun_receive], node->nod_import); } static DSC *execute_prompt( QLI_NOD node) { /************************************** * * e x e c u t e _ p r o m p t * ************************************** * * Functional description * Execute a prompt and return a descriptor. If end of file, * abort request; if a singleton tab, return the previous value, * and if a null string, return MISSING. * **************************************/ TEXT *p, string[128], *value, buffer[256]; DSC *desc; USHORT reprompt; VARY *data; int length, l; if (sw_forms) FORM_reset(); ERRQ_pending(); reprompt = QLI_reprompt; desc = &node->nod_desc; data = (VARY *) desc->dsc_address; value = (desc->dsc_length - 2 <= sizeof(buffer)) ? buffer : data->vary_string; length = (desc->dsc_length - 2 <= sizeof(buffer)) ? sizeof(buffer) : desc->dsc_length - 2; for (;;) { ++QLI_prompt_count; if (node->nod_arg[e_prm_prompt]) { if (reprompt) sprintf(string, "\07%s %s: ", prompt[0], node->nod_arg[e_prm_prompt]); else sprintf(string, "%s %s: ", prompt[1], node->nod_arg[e_prm_prompt]); } else { if (reprompt) sprintf(string, "\07%s: ", prompt[0]); /* Msg497 Re-enter */ else sprintf(string, "%s: ", prompt[1]); /* Msg498 Enter */ } if (!LEX_get_line(string, value, length)) { QLI_abort = TRUE; EXEC_poll_abort(); } if (value[0] == '\t' && value[1] == '\n') return desc; p = value + strlen(value); if (p > value && p[-1] == '\n') *--p = 0; /* Get rid of trailing blanks on non-text data types */ if (desc->dsc_dtype > dtype_varying) { while (p > value && p[-1] == ' ') --p; *p = 0; } if ((l = p - value) <= desc->dsc_length - 2) { if (value != data->vary_string) memcpy(data->vary_string, value, l); desc->dsc_missing = (data->vary_length = l) ? 0 : DSC_missing; return desc; } ERRQ_msg_put(32, NULL, NULL, NULL, NULL, NULL); /* Msg32 Input value is too long */ reprompt = TRUE; } } static DSC *execute_statistical( QLI_NOD node) { /************************************** * * e x e c u t e _ s t a t i s t i c a l * ************************************** * * Functional description * Execute a statistical expression. * **************************************/ QLI_REQ request; QLI_MSG message; /* If there is a request associated with the node, start it and possibly send a message along with it. */ if (request = (QLI_REQ) node->nod_arg[e_stt_request]) EXEC_start_request(request, (QLI_MSG) node->nod_arg[e_stt_send]); else if (message = (QLI_MSG) node->nod_arg[e_stt_send]) EXEC_send(message); return EXEC_receive((QLI_MSG) node->nod_arg[e_stt_receive], node->nod_import); } static int like( UCHAR * p1, SSHORT l1, UCHAR * p2, SSHORT l2, UCHAR escape_char) { /************************************** * * l i k e * ************************************** * * Functional description * Return true if a string (p1, l1) matches a given pattern (p2, l2). * The character '_' in the pattern may match any single character * in the the string, and the character '%' may match any sequence * of characters. * Japanese version operates on short-based buffer, * instead of char-based. * **************************************/ UCHAR c; int escape; escape = FALSE; while (--l2 >= 0) { c = *p2++; if (escape_char && !escape && c == escape_char) { escape = TRUE; continue; } if (!escape && c == '%') { if (l2 == 0) return TRUE; while (l1) if (like(p1++, l1--, p2, l2, escape_char)) return TRUE; return FALSE; } if (--l1 < 0) return FALSE; if ((escape || c != '_') && c != *p1) return FALSE; escape = FALSE; p1++; } return (l1) ? FALSE : TRUE; } static TEXT *make_blob_buffer( FRBRD * blob, USHORT * length) { /************************************** * * m a k e _ b l o b _ b u f f e r * ************************************** * * Functional description * For a blob, get the max segment length, * check that the buffer allocated is at least * that length, and, if not, allocate a bigger one. * **************************************/ SLONG size, segment_count, max_segment; gds__blob_size(&blob, &size, &segment_count, &max_segment); if (max_segment >= *length) { TEXT *buffer; *length = max_segment; buffer = (TEXT *) gds__alloc((SLONG) * length); #ifdef DEBUG_GDS_ALLOC /* We don't care about QLI specific memory leaks for V4.0 */ gds_alloc_flag_unfreed((void *) buffer); /* QLI: don't care */ #endif return buffer; } return 0; } static int matches( TEXT * p1, SSHORT l1, TEXT * p2, SSHORT l2) { /************************************** * * m a t c h e s * ************************************** * * Functional description * Return true if a string (p1, l1) matches a given pattern (p2, l2). * The character '?' in the pattern may match any single character * in the the string, and the character '*' may match any sequence * of characters. * **************************************/ TEXT c; while (--l2 >= 0) { c = *p2++; if (c == '*') { if (l2 == 0) return TRUE; while (l1) if (matches(p1++, l1--, p2, l2)) return TRUE; return FALSE; } if (--l1 < 0 || (c != '?' && UPPER(c) != UPPER(*p1))) return FALSE; p1++; } return (l1) ? FALSE : TRUE; } static int sleuth( QLI_NOD node, DSC * desc1, DSC * desc2, DSC * desc3) { /************************************** * * s l e u t h * ************************************** * * Functional description * Return true if a string (p1, l1) matches a given pattern (p2, l2), * using a pattern language defined in p3, l3. * **************************************/ TEXT *p1, *p2, *buffer, temp1[TEMP_LENGTH], temp2[TEMP_LENGTH], fixed_buffer[512], control[256]; FRBRD *blob; USHORT buffer_length; STATUS status_vector[ISC_STATUS_LENGTH]; SSHORT l1, l2, result; QLI_CTX context; QLI_REQ request; DBB dbb; /* Get operator definition string (control string) */ l1 = MOVQ_get_string(desc3, &p1, (VARY*) temp1, TEMP_LENGTH); /* Get address and length of search string */ l2 = MOVQ_get_string(desc2, &p2, (VARY*) temp2, TEMP_LENGTH); /* Merge search and control strings */ l2 = sleuth_merge((UCHAR*) p2, (UCHAR*) (p2 + l2), (UCHAR*) p1, (UCHAR*) (p1 + l1), (UCHAR*) control); /* If source is not a blob, do a simple search */ if (desc1->dsc_dtype != dtype_blob) { l1 = MOVQ_get_string(desc1, &p1, (VARY*) temp1, TEMP_LENGTH); return sleuth_check(0, (UCHAR*) p1, (UCHAR*) (p1 + l1), (UCHAR*) control, (UCHAR*) (control + l2)); } /* Source string is a blob, things get interesting */ result = FALSE; blob = EXEC_open_blob(node->nod_arg[0]); buffer_length = sizeof(fixed_buffer); if (!(buffer = make_blob_buffer( blob, &buffer_length))) buffer = fixed_buffer; while (!gds__get_segment(status_vector, GDS_REF(blob), (USHORT*) GDS_REF(l1), buffer_length, GDS_VAL(buffer))) if (sleuth_check(0, (UCHAR*) buffer, (UCHAR*) (buffer + l1), (UCHAR*) control, (UCHAR*) (control + l2))) { result = TRUE; break; } if (buffer != fixed_buffer) gds__free(buffer); if (gds__close_blob(status_vector, GDS_REF(blob))) { context = (QLI_CTX) node->nod_arg[e_fld_context]; request = context->ctx_request; dbb = request->req_database; ERRQ_database_error(dbb, status_vector); } return result; } static int sleuth_check( USHORT flags, UCHAR * search, UCHAR * end_search, UCHAR * match, UCHAR * end_match) { /************************************** * * s l e u t h _ c h e c k * ************************************** * * Functional description * Evaluate the "sleuth" search operator. * **************************************/ UCHAR c, d, *class_, *end_class; while (match < end_match) { c = *match++; if ((c == '@' && (c = *match++)) || !special[c]) { c = COND_UPPER(c); if (match >= end_match || *match != '*') { if (search >= end_search) return FALSE; d = *search++; if (c != COND_UPPER(d)) return FALSE; } else { ++match; for (;;) if (sleuth_check (flags, search, end_search, match, end_match)) return TRUE; else if (search < end_search) { d = *search++; if (c != COND_UPPER(d)) return FALSE; } else return FALSE; } } else if (c == '?') if (match >= end_match || *match != '*') { if (search >= end_search) return FALSE; search++; } else { if (++match >= end_match) return TRUE; for (;;) if (sleuth_check (flags, search, end_search, match, end_match)) return TRUE; else if (++search >= end_search) return FALSE; } else if (c == '[') { class_ = match; while (*match++ != ']') if (match >= end_match) return FALSE; end_class = match - 1; if (match >= end_match || *match != '*') { if (!sleuth_class(flags, class_, end_class, *search++)) return FALSE; } else { ++match; for (;;) if (sleuth_check (flags, search, end_search, match, end_match)) return TRUE; else if (search < end_search) { if (!sleuth_class (flags, class_, end_class, *search++)) return FALSE; } else return FALSE; } } else if (c == '+') { c = *match++; if (c == 'S' || c == 's') flags &= ~SLEUTH_insensitive; } else if (c == '-') { c = *match++; if (c == 'S' || c == 's') flags |= SLEUTH_insensitive; } } if (search < end_search) return FALSE; return TRUE; } static int sleuth_class( USHORT flags, UCHAR * class_, UCHAR * end_class, UCHAR character) { /************************************** * * s l e u t h _ c l a s s * ************************************** * * Functional description * See if a character is a member of a class. * **************************************/ UCHAR c; USHORT result; result = TRUE; character = COND_UPPER(character); if (*class_ == '~') { ++class_; result = FALSE; } while (class_ < end_class) { c = *class_++; if (c == '@') { if (*class_++ == character) return TRUE; } else if (*class_ == '-') { class_ += 2; if (character >= c && character <= class_[-1]) return result; } else if (character == c) return result; } return (result) ? FALSE : TRUE; } static int sleuth_merge( UCHAR * match, UCHAR * end_match, UCHAR * control, UCHAR * end_control, UCHAR * combined) { /************************************** * * s l e u t h _ m e r g e * ************************************** * * Functional description * Merge the matching pattern and control strings to give a cannonical * matching pattern. Return the length of the combined string. * * What this routine does is to take the language template, strip off * the prefix and put it in the output string, then parse the definitions * into an array of character pointers. The index array is the defined * character. The routine then takes the actual match pattern and uses * the characters in it to index into the definitions to produce an equivalent * pattern in the cannonical language. * * The silly loop setting *v++ to zero initializes the array up to the * highest character defined (also max_op). Believe it or not, that part * is not a bug. * **************************************/ UCHAR c, *comb, **v, *vector[128], **end_vector, *p, max_op, temp[256], *t; comb = combined; v = vector; t = temp; /* Parse control string into substitution strings and initializing string */ while (control < end_control) { c = *control++; if (*control == '=') { end_vector = vector + c; while (v <= end_vector) *v++ = 0; *end_vector = t; ++control; while (control < end_control) { c = *control++; if ((t[-1] == '@') || ((c != ',') && (c != ')'))) *t++ = c; else break; } *t++ = 0; } else if (c == '@' && control < end_control) *comb++ = *control++; else if (c == ')') break; else if (c != '(') *comb++ = c; } max_op = v - vector; /* Interpret matching string, substituting where appropriate */ while (c = *match++) { /* if we've got a defined character, slurp the definition */ if (c <= max_op && (p = vector[c])) { while (*p) *comb++ = *p++; /* if we've got the definition of a quote character, slurp the next character too */ if (comb[-1] == '@' && *match) *comb++ = *match++; } /* at this point we've got a non-match, but as it might be one of ours, quote it. */ else { if (special[c] && comb[-1] != '@') *comb++ = '@'; *comb++ = c; } } /* Put in trailing stuff */ while (control < end_control) *comb++ = *control++; return comb - combined; } static int string_boolean( QLI_NOD node) { /************************************** * * s t r i n g _ b o o l e a n * ************************************** * * Functional description * Perform one of the complex string functions CONTAINING, MATCHES, * or STARTS WITH. * **************************************/ DSC *desc1, *desc2, *desc3; TEXT *p1, *p2, *buffer, fixed_buffer[512]; TEXT temp1[TEMP_LENGTH], temp2[TEMP_LENGTH]; FRBRD *blob; SSHORT l1, l2, result; USHORT buffer_length; STATUS status_vector[ISC_STATUS_LENGTH]; QLI_CTX context; QLI_REQ request; DBB dbb; if (!(desc1 = EVAL_value(node->nod_arg[0])) || (desc1->dsc_missing & DSC_missing) || !(desc2 = EVAL_value(node->nod_arg[1])) || (desc2->dsc_missing & DSC_missing) || (node->nod_arg[2] && (!(desc3 = EVAL_value(node->nod_arg[2])) || (desc3->dsc_missing & DSC_missing)))) return FALSE; if (node->nod_type == nod_sleuth) return sleuth(node, desc1, desc2, desc3); /* Get address and length of strings */ l2 = MOVQ_get_string(desc2, &p2, (VARY*) temp2, TEMP_LENGTH); /* If source is not a blob, do a simple search */ if (desc1->dsc_dtype != dtype_blob) { l1 = MOVQ_get_string(desc1, &p1, (VARY*) temp1, TEMP_LENGTH); return string_function(node, l1, p1, l2, p2); } /* Source string is a blob, things get interesting */ result = FALSE; blob = EXEC_open_blob(node->nod_arg[0]); buffer_length = sizeof(fixed_buffer); if (!(buffer = make_blob_buffer( blob, &buffer_length))) buffer = fixed_buffer; while (!gds__get_segment(status_vector, GDS_REF(blob), (USHORT*) GDS_REF(l1), buffer_length, GDS_VAL(buffer))) if (string_function(node, l1, buffer, l2, p2)) { result = TRUE; break; } if (buffer != fixed_buffer) gds__free(buffer); if (gds__close_blob(status_vector, GDS_REF(blob))) { context = (QLI_CTX) node->nod_arg[e_fld_context]; request = context->ctx_request; dbb = request->req_database; ERRQ_database_error(dbb, status_vector); } return result; } static int string_function( QLI_NOD node, SSHORT l1, TEXT * p1, SSHORT l2, TEXT * p2) { /************************************** * * s t r i n g _ f u n c t i o n * ************************************** * * Functional description * Perform one of the complex string functions CONTAINING, MATCHES, * or STARTS WITH. * **************************************/ TEXT *q1, *q2, c1, c2, temp[16]; SSHORT l; /* Handle "STARTS WITH" */ if (node->nod_type == nod_starts) { if (l1 < l2) return FALSE; while (--l2 >= 0) if (*p1++ != *p2++) return FALSE; return TRUE; } /* Handle CONTAINS */ if (node->nod_type == nod_containing) { while (l1 >= l2) { --l1; q1 = p1++; q2 = p2; l = l2; do { if (--l < 0) return TRUE; c1 = *q1++; c2 = *q2++; } while (UPPER(c1) == UPPER(c2)); } return FALSE; } /* Handle LIKE */ if (node->nod_type == nod_like) { c1 = 0; if (node->nod_count > 2 && MOVQ_get_string(EVAL_value(node->nod_arg[2]), &q1, (VARY*) temp, sizeof(temp))) c1 = *q1; if (like((UCHAR*) p1, l1, (UCHAR*) p2, l2, c1)) return TRUE; return FALSE; } /* Handle MATCHES */ if (node->nod_type == nod_matches) if (matches(p1, l1, p2, l2)) return TRUE; return FALSE; }