8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 20:03:03 +01:00
firebird-mirror/src/jrd/map.cpp
2002-11-19 12:37:11 +00:00

959 lines
20 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: map.c
* DESCRIPTION: Map GALAXY blr into RDB blr
*
* 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 ssdef
#include descrip
#include "../jrd/gds.h"
#include "../jrd/common.h"
#include "../jrd/gdsassert.h"
#include "../jrd/gds_proto.h"
#define BUGCHECK bugcheck
#define RDB$_SEGSTR_EOF 20480378
#define RDB$_SEGMENT 20480369
#define GDS$_MISC_INTERPRETED 1
/* Struct used to describe records that request mapping */
typedef struct map_msg {
struct msg *msg_next; /* Next message in request */
USHORT msg_number; /* Message number */
USHORT msg_gds_length; /* Length of GDS form */
USHORT msg_rdb_length; /* Length of RDB form */
USHORT msg_count; /* Number of fields */
struct msg_repeat {
USHORT msg_dtype; /* Parameter dtype */
USHORT msg_length; /* Parameter length */
} msg_rpt[];
} *MAP_MSG;
#include "../jrd/map_proto.h"
extern double MTH$CVT_D_G(), MTH$CVT_G_D();
static void bugcheck(int);
static BOOLEAN check_message(UCHAR **);
static MAP_MSG rebuild_message(UCHAR **, UCHAR **);
static int translate_status(STATUS *, STATUS *, SCHAR **);
#define WRKBUF_SIZ 256
#define DEFAULT_STATUS_FLAGS 0xF0000L
static SLONG user_codes[] = {
1,
#include "../include/gen/rdb_codes.h"
};
static SCHAR workbuf[1024];
void MAP_date_to_gds(SLONG * vms_date, SLONG * gds_date)
{
/**************************************
*
* M A P _ d a t e _ t o _ g d s
*
**************************************
*
* Functional description
* Map a date to GDS date format from VMS format.
*
**************************************/
SLONG time;
lib$day(gds_date, vms_date, &time);
gds_date[1] = time * 100;
}
void MAP_date_to_rdb(SLONG * gds_date, SLONG * vms_date)
{
/**************************************
*
* M A P _ d a t e _ t o _ r d b
*
**************************************
*
* Functional description
* Map a date to VMS date format from GDS format.
*
**************************************/
SLONG time[2], zero[2], temp, temp2;
/* Compute time portion */
zero[0] = zero[1] = 0;
temp = 1000;
lib$emul(gds_date + 1, &temp, zero, time);
/* Compute date portion being careful of overflow */
temp = 60 * 10000000;
temp2 = gds_date[0] * 24 * 60;
lib$emul(&temp2, &temp, time, vms_date);
}
int MAP_gds_to_rdb(USHORT number, MAP_MSG msg, UCHAR * from, UCHAR * to)
{
/**************************************
*
* M A P _ g d s _ t o _ r d b
*
**************************************
*
* Functional description
* Given a message number, a GDS format record, and a buffer
* to hold an Rdb format record, check to see if a map record
* exists for the message, and if so do the mapping. If there
* is not a map record, return 0. If there is, return the length
* of the Rdb record.
*
**************************************/
VARY vary;
UCHAR *p, *q;
USHORT l;
struct msg_repeat *desc, *end;
/* Find the message. If none, return 0 and get out */
for (; msg; msg = msg->msg_next)
if (msg->msg_number == number)
break;
if (!msg)
return 0;
/* Map data. Whoppee */
for (desc = msg->msg_rpt, end = desc + msg->msg_count; desc < end; desc++)
switch (desc->msg_dtype) {
case blr_cstring:
q = from;
from += desc->msg_length;
vary = to;
to += desc->msg_length + 1;
l = strlen(q);
if (l > desc->msg_length - 1)
l = desc->msg_length - 1;
if (vary->vary_length = l) {
p = vary->vary_data;
do
*p++ = *q++;
while (--l);
}
break;
case blr_timestamp:
MAP_date_to_rdb(from, to);
from += desc->msg_length;
to += desc->msg_length;
break;
case blr_d_float:
*((double *) to) = MTH$CVT_D_G((double *) from);
from += desc->msg_length;
to += desc->msg_length;
break;
default:
if (l = desc->msg_length)
do
*to++ = *from++;
while (--l);
}
return msg->msg_rdb_length;
}
MAP_MSG MAP_parse_blr(UCHAR * org_blr,
USHORT org_blr_length,
UCHAR * new_blr,
USHORT * new_blr_length, SLONG * max_length)
{
/**************************************
*
* M A P _ p a r s e _ b l r
*
**************************************
*
* Functional description
* The price of progress is incompatibility. GALAXY supports null
* terminated strings, but the Rdb world does not. To bridge the
* incompatibility, search out messages that contain verboten data
* types and build message blocks for the offending messages.
*
**************************************/
MAP_MSG msg, next;
UCHAR *end_org_blr, *new, *org, *last_copied;
SLONG max;
end_org_blr = org_blr + org_blr_length;
org = last_copied = org_blr;
new = new_blr;
msg = NULL;
/* Check version. It must be version 4 or 5 or else we can't handle this */
if (*org != blr_version4 && *org != blr_version5)
return NULL;
++org;
if (*org++ != blr_begin)
return NULL;
/* Process messages until something else is entered. If the message
requires translation, build both a msg mapping block and a new
blr message */
while (*org++ == blr_message)
if (check_message(&org)) {
while (last_copied < org)
*new++ = *last_copied++;
if (!(next = rebuild_message(&org, &new)))
return NULL;
next->msg_next = msg;
msg = next;
last_copied = org;
}
/* If we've process all messages and none require work, indicate that
nothing remains to be done and get out */
for (max = 0, next = msg; next; next = next->msg_next) {
max = MAX(max, next->msg_gds_length);
max = MAX(max, next->msg_rdb_length);
}
*max_length = max;
if (!msg)
return NULL;
/* The request needs to be rebuilt completely. We've already copied the
messages, now finish the rest of the request. */
while (last_copied < end_org_blr)
*new++ = *last_copied++;
*new_blr_length = new - new_blr;
return msg;
}
int MAP_rdb_length(USHORT number, MAP_MSG msg)
{
/**************************************
*
* M A P _ r d b _ l e n g t h
*
**************************************
*
* Functional description
* Determine where a particular message type requires mapping,
* and if so, the Rdb length.
*
**************************************/
for (; msg; msg = msg->msg_next)
if (msg->msg_number == number)
return msg->msg_rdb_length;
return 0;
}
int MAP_rdb_to_gds(USHORT number, MAP_MSG msg, UCHAR * from, UCHAR * to)
{
/**************************************
*
* M A P _ r d b _ t o _ g d s
*
**************************************
*
* Functional description
* Given a message number, an Rdb format record, and a buffer
* to hold an GDS format record, check to see if a map record
* exists for the message, and if so do the mapping. If there
* is not a map record, return 0. If there is, return the length
* of the GDS record.
*
**************************************/
VARY vary;
UCHAR *p, *q;
USHORT l;
struct msg_repeat *desc, *end;
/* Find the message. If none, return 0 and get out */
for (; msg; msg = msg->msg_next)
if (msg->msg_number == number)
break;
if (!msg)
return 0;
/* Map data. Whoppee */
for (desc = msg->msg_rpt, end = desc + msg->msg_count; desc < end; desc++)
switch (desc->msg_dtype) {
case blr_cstring:
vary = from;
from += desc->msg_length + 1;
p = to;
to += desc->msg_length;
l = MIN(vary->vary_length, desc->msg_length - 1);
if (l) {
q = vary->vary_data;
do
*p++ = *q++;
while (--l);
}
*p = 0;
break;
case blr_timestamp:
MAP_date_to_gds(from, to);
from += desc->msg_length;
to += desc->msg_length;
break;
case blr_d_float:
*((double *) to) = MTH$CVT_G_D((double *) from);
from += desc->msg_length;
to += desc->msg_length;
break;
default:
if (l = desc->msg_length)
do
*to++ = *from++;
while (--l);
}
return msg->msg_gds_length;
}
void MAP_release(MAP_MSG msg)
{
/**************************************
*
* M A P _ r e l e a s e
*
**************************************
*
* Functional description
* Release any mapping blocks.
*
**************************************/
MAP_MSG next;
while (next = msg) {
msg = msg->msg_next;
gds__free(next);
}
}
int MAP_status_to_gds(STATUS * vms_status, STATUS * gds_status)
{
/**************************************
*
* M A P _ s t a t u s _ t o _ g d s
*
**************************************
*
* Functional description
* Translate an RDB status vector to a GDS error status vector.
*
**************************************/
STATUS code, number, length, gds_index, *tmp, temp[ISC_STATUS_LENGTH];
USHORT shift, flag;
SCHAR msgbuff[WRKBUF_SIZ], *p, *q, *pw1, *pw2, flags[4], part;
struct dsc$descriptor_s desc, *dsc_ptr;
static SCHAR *messages[] = {
#include "../jrd/msgs.h"
0
};
tmp = temp;
pw1 = pw2 = workbuf;
gds_status[0] = gds_arg_gds;
code = vms_status[1];
if (code == 1)
return (gds_status[1] = FB_SUCCESS);
p = msgbuff;
length = 0;
desc.dsc$b_class = DSC$K_CLASS_S;
desc.dsc$b_dtype = DSC$K_DTYPE_T;
desc.dsc$w_length = WRKBUF_SIZ;
desc.dsc$a_pointer = p;
sys$getmsg(code, &length, &desc, 15, flags);
p[length] = 0;
gds_index = (code >> 3) - 2560000;
if (gds_index < 0 || gds_index > gds_err_max) {
gds_status[1] = gds__random;
gds_status[2] = gds_arg_string;
gds_status[3] = pw1;
gds_status[4] = gds_arg_end;
while (*p)
*pw2++ = *p++;
return gds__random;
}
flag = FALSE;
vms_status = vms_status + 3;
if (*p == '%')
p++;
while (*pw2++ = *p) {
if (*p++ != '!')
continue;
pw2--;
switch (*p++) {
case 'O':
case 'Z':
case 'S':
BUGCHECK(234); /* msg 234 Unimplemented conversion, FAO directive O,Z,S */
case 'X':
case 'U':
if (*p++ != 'L')
BUGCHECK(235); /* msg 235 Unimplemented conversion, FAO directive X,U */
*tmp++ = gds_arg_number;
number = *tmp++ = *vms_status++;
flag = (number == 1) ? FALSE : TRUE;
sprintf(pw2, "%%x%x", number);
while (*pw2)
++pw2;
break;
case 'A':
if (*p == 'C') {
q = *vms_status++;
length = *q++;
}
else if (*p == 'D' || *p == 'F') {
length = *vms_status++;
q = *vms_status++;
}
else if (*p == 'S') {
dsc_ptr = *vms_status++;
length = dsc_ptr->dsc$w_length;
q = dsc_ptr->dsc$a_pointer;
}
else
BUGCHECK(236); /* msg 236 Error parsing RDB FAO msg string */
++p;
*tmp++ = gds_arg_cstring;
*tmp++ = length;
*tmp++ = pw2;
while (length--)
*pw2++ = *q++;
break;
case '%':
if (*p != 'S')
*pw2++ = *p;
else if (flag)
*pw2++ = 's';
p++;
break;
case '!':
*pw2++ = '!';
break;
#ifdef DEBUG
case '-':
case '+':
case '*':
case '>':
case '<':
case '/':
case '_':
case '^':
break;
default:
BUGCHECK(237); /* msg 237 Error parsing RDB FAO msg str */
#endif
}
}
/* So now we've parsed the message & made it into a string.
Further, we've neatly saved the args in order in tmp...!
Get gds_message into buffer, parse & fudge parms*/
gds_status[1] = code = gds__encode(gds_index, 0);
p = messages[gds_index];
tmp = temp;
gds_status = gds_status + 2;
flag = FALSE;
while (*p) {
if (*p++ != '%')
continue;
switch (*p) {
case 's':
*gds_status++ = gds_arg_cstring;
if (*tmp++ != gds_arg_cstring || flag) {
flag = TRUE;
*gds_status++ = 3;
*gds_status++ = pw2;
sprintf(pw2, "<?>");
break;
}
*gds_status++ = *tmp++;
*gds_status++ = *tmp++;
break;
case 'l':
++p;
case 'd':
*gds_status++ = gds_arg_number;
if (*tmp++ != gds_arg_number || flag) {
flag = TRUE;
*gds_status++ = -1;
break;
}
*gds_status++ = *tmp++;
break;
default:
BUGCHECK(238); /* msg 238 unknown parameter in RdB status vector */
}
}
*gds_status++ = gds_arg_interpreted;
*gds_status++ = pw1;
*gds_status++ = gds_arg_end;
return code;
}
int MAP_status_to_rdb(STATUS * status_vector, STATUS * user_status)
{
/**************************************
*
* M A P _ s t a t u s _ t o _ r d b
*
**************************************
*
* Functional description
* Translate a status vector from the Groton world to the RDB world.
* Continue until the end of the GDS vector is reached or until
* we hit a code that we don't know.
*
**************************************/
STATUS *gds, *rdb;
SLONG total_longwds, ret_value;
SCHAR *buf_ptr;
total_longwds = 0;
gds = status_vector;
rdb = user_status + 1;
buf_ptr = workbuf;
while (*gds)
if (ret_value = translate_status(&gds, &rdb, &buf_ptr))
total_longwds += ret_value += 2;
else {
total_longwds += 2;
break;
}
user_status[0] = DEFAULT_STATUS_FLAGS + total_longwds; /* Hack, hack, hack... */
return user_status[1];
}
static void bugcheck(int number)
{
/**************************************
*
* b u g c h e c k
*
**************************************
*
* Functional description
* output an error message
*
**************************************/
int n;
SCHAR buffer[128], number_text[20];
USHORT flags;
n =
gds__msg_lookup(0, JRD_BUGCHK, number, sizeof(buffer), buffer,
&flags);
if (n < 1)
strcpy(buffer, "error code");
sprintf(number_text, " (%d)", number);
strcat(buffer, number_text);
gds__log(buffer);
}
static BOOLEAN check_message(UCHAR ** org_ptr)
{
/**************************************
*
* c h e c k _ m e s s a g e
*
**************************************
*
* Functional description
* Parse a blr message. If the message contains a non-Rdb data type,
* return TRUE. Otherwise update the blr pointer and return FALSE.
*
**************************************/
UCHAR *org, dtype;
SSHORT count;
/* Throw away the message number then pick up the number of fields */
org = *org_ptr + 1;
count = *org++;
count |= *org++ << 16;
while (count--)
switch (dtype = *org++) {
case blr_timestamp:
case blr_cstring:
case blr_d_float:
return TRUE;
case blr_text:
case blr_varying:
*org++;
case blr_short:
case blr_long:
case blr_quad:
*org++;
}
*org_ptr = org;
return FALSE;
}
static MAP_MSG rebuild_message(UCHAR ** org_ptr, UCHAR ** new_ptr)
{
/**************************************
*
* r e b u i l d _ m e s s a g e
*
**************************************
*
* Functional description
* A message contains an offensive gds datatype. Rebuild it as a
* valid Rdb message while building a msg block to drive the data
* mapping. If we succeed, return the MAP_MSG block. If we fail for
* any reason, return NULL;
*
**************************************/
MAP_MSG msg;
UCHAR *org, *new, *last_copied;
USHORT number, l, length, gds_length, rdb_length;
struct msg_repeat *desc, *end;
/* Do some housekeeping then pick up message number and count of parameters */
length = 0;
org = last_copied = *org_ptr;
new = *new_ptr;
number = *org++;
l = *org++;
l |= *org++ << 16;
msg = gds__alloc((SLONG)
(sizeof(struct msg) + l * sizeof(struct msg_repeat)));
/* FREE: by MAP_release() */
if (!msg)
return NULL; /* NO MEM */
msg->msg_number = number;
msg->msg_count = l;
msg->msg_gds_length = 0;
msg->msg_rdb_length = 0;
for (desc = msg->msg_rpt, end = desc + l; desc < end; desc++) {
switch (desc->msg_dtype = *org++) {
case blr_text:
gds_length = *org++;
gds_length += *org++ << 16;
rdb_length = gds_length;
break;
case blr_varying:
gds_length = 2 + *org++;
gds_length += *org++ << 16;
rdb_length = gds_length;
break;
case blr_short:
*org++;
rdb_length = gds_length = 2;
break;
case blr_long:
*org++;
case blr_float:
rdb_length = gds_length = 4;
break;
case blr_quad:
*org++;
case blr_timestamp:
case blr_double:
rdb_length = gds_length = 8;
break;
case blr_d_float:
while (last_copied < org)
*new++ = *last_copied++;
new[-1] = blr_double;
rdb_length = gds_length = 8;
break;
case blr_cstring:
while (last_copied < org)
*new++ = *last_copied++;
new[-1] = blr_varying;
gds_length = *org++;
gds_length |= *org++ << 16;
rdb_length = gds_length + 1;
l = rdb_length - 2;
*new++ = l;
*new++ = l >> 16;
last_copied = org;
break;
default:
return NULL;
}
desc->msg_length = gds_length;
msg->msg_gds_length += gds_length;
msg->msg_rdb_length += rdb_length;
}
/* We're done processing parameters. Copy anything passed over, update
pointers, and return */
while (last_copied < org)
*new++ = *last_copied++;
*org_ptr = org;
*new_ptr = new;
return msg;
}
static int translate_status(
STATUS * status_vector,
STATUS * user_status, SCHAR ** buf_ptr)
{
/**************************************
*
* t r a n s l a t e _ s t a t u s
*
**************************************
*
* Functional description
* Translate a single status message.
* Return the number of FAO arguments.
* If we don't know the status code, return it to caller.
*
**************************************/
STATUS *gds, *rdb, code, *count_addr;
USHORT fac = 0, class_ = 0;
SSHORT count, length;
SCHAR msgbuff[WRKBUF_SIZ], *p, *q, *pw1, *pw2, flags[4];
struct dsc$descriptor_s desc, *desc_ptr;
p = msgbuff;
pw1 = pw2 = *buf_ptr;
gds = *status_vector;
rdb = *user_status;
count = 0;
if (*gds == gds_arg_interpreted) {
q = gds[1];
rdb[0] = GDS$_MISC_INTERPRETED;
rdb[1] = DEFAULT_STATUS_FLAGS + 1;
rdb[2] = strlen(q);
rdb[3] = gds[1];
return 2;
}
if (*gds++ != gds_arg_gds)
BUGCHECK(239); /* msg 239 Interbase status vector inconsistent */
code = gds__decode(*gds, &fac, &class_);
if ((code < 0) || (code > gds_err_max)) {
rdb[0] = *gds;
rdb[1] = DEFAULT_STATUS_FLAGS;
return 0;
}
code = *rdb++ = user_codes[code];
length = 0;
desc.dsc$b_class = DSC$K_CLASS_S;
desc.dsc$b_dtype = DSC$K_DTYPE_T;
desc.dsc$w_length = WRKBUF_SIZ;
desc.dsc$a_pointer = p;
sys$getmsg(code, &length, &desc, 15, flags);
p[length] = 0;
count_addr = rdb++;
++gds;
/* GDS message parameters should be the same in number, order & datatype
as those RDB expects. If not, be real unpleasant about it. */
while (*p) {
if (*p++ != '!')
continue;
switch (*p) {
case 'O':
case 'X':
case 'Z':
case 'U':
case 'S':
if ((*gds++) != gds_arg_number)
BUGCHECK(240); /* msg 240 Interbase/RdB message parameter inconsistency */
*rdb++ = *gds++;
++count;
break;
case 'A':
++p;
switch (*gds++) {
case gds_arg_cstring:
length = *pw2++ = *gds++;
q = *gds++;
do
(*pw2++ = *q++);
while (--length);
break;
case gds_arg_string:
++pw2;
length = 0;
q = *gds++;
while (*pw2++ = *q++)
++length;
*pw1 = length;
break;
default:
BUGCHECK(240); /* msg 240 Interbase/RdB message parameter inconsistency */
}
if (*p == 'C')
*rdb++ = pw1;
else if (*p == 'D' || *p == 'F') {
*rdb++ = *pw1++;
*rdb++ = pw1;
++count;
}
else if (*p == 'S') {
*rdb++ = pw2;
desc_ptr = (struct dsc$descriptor *) pw2;
desc_ptr->dsc$b_class = DSC$K_CLASS_S;
desc_ptr->dsc$b_dtype = DSC$K_DTYPE_T;
desc_ptr->dsc$w_length = *pw1++;
desc_ptr->dsc$a_pointer = pw1;
pw2 += sizeof(struct dsc$descriptor);
}
else
BUGCHECK(241); /* msg 241 error parsing RDB FAO message string */
pw1 = pw2;
++count;
break;
case '%':
++p;
if (*p == 'S')
break;
BUGCHECK(242); /* msg 242 unimplemented FAO directive */
case '!':
++p;
break;
#ifdef DEBUG
case '-':
case '+':
break;
case '*':
case '>':
case '<':
case '/':
case '_':
case '^':
++p;
break;
default:
BUGCHECK(241); /* msg 241 error parsing RDB FAO message string */
#endif
}
}
*buf_ptr = pw2;
count_addr[0] = DEFAULT_STATUS_FLAGS + count;
*status_vector = gds;
*user_status = rdb;
return count;
}