mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-26 08:03:03 +01:00
1abb10f36d
-add space before XXXFORMAT macros to not confuse it with user-defined literals -use existing snprintf This should fix part of the issues at CORE-5099 and CORE-5120
1645 lines
33 KiB
C++
1645 lines
33 KiB
C++
/*
|
|
* PROGRAM: QLI Access Method
|
|
* MODULE: mov.cpp
|
|
* DESCRIPTION: Data mover and converter and comparator, etc.
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <string.h>
|
|
#include "../qli/dtr.h"
|
|
#include "../common/classes/timestamp.h"
|
|
#include "../jrd/intl.h"
|
|
#include "../qli/err_proto.h"
|
|
#include "../yvalve/gds_proto.h"
|
|
#include "../common/gdsassert.h"
|
|
#include "../qli/mov_proto.h"
|
|
#include "../common/utils_proto.h"
|
|
|
|
using MsgFormat::SafeArg;
|
|
|
|
|
|
static void date_error(const TEXT*, const USHORT);
|
|
static double double_from_text(const dsc* desc);
|
|
static void sql_date_to_text(const SLONG[1], DSC*);
|
|
static void sql_time_to_text(const ULONG[1], DSC*);
|
|
static void timestamp_to_text(const SLONG[2], DSC*);
|
|
static void mover_error(int, USHORT, USHORT);
|
|
static void now_to_date(const tm*, SLONG[2]);
|
|
static void numeric_to_text(const dsc*, dsc*);
|
|
static void string_to_date(const TEXT*, USHORT, SLONG[2]);
|
|
static void string_to_time(const TEXT*, USHORT, SLONG[2]);
|
|
static const TEXT* type_name(USHORT);
|
|
|
|
|
|
|
|
#define LETTER(c) (c >= 'A' && c <= 'Z')
|
|
#define DIGIT(c) (c >= '0' && c <= '9')
|
|
const char* const TODAY = "TODAY";
|
|
const char* const NOW = "NOW";
|
|
const char* const TOMORROW = "TOMORROW";
|
|
const char* const YESTERDAY = "YESTERDAY";
|
|
|
|
const int PRECISION = 10000;
|
|
|
|
struct dtypes_t
|
|
{
|
|
USHORT type;
|
|
const TEXT* description;
|
|
};
|
|
|
|
static const dtypes_t dtypes_table[] =
|
|
{
|
|
{ dtype_unknown, "NULL" },
|
|
{ dtype_text, "character string" },
|
|
{ dtype_cstring, "character string" },
|
|
{ dtype_varying, "varying string" },
|
|
{ dtype_packed, "packed decimal" },
|
|
{ dtype_byte, "byte integer" },
|
|
{ dtype_short, "short integer" },
|
|
{ dtype_long, "long integer" },
|
|
{ dtype_quad, "quadword integer" },
|
|
{ dtype_real, "single precision floating" },
|
|
{ dtype_double, "double precision floating" },
|
|
{ dtype_d_float, "double precision floating" },
|
|
{ dtype_timestamp, "date" },
|
|
{ dtype_sql_date, "SQL date" },
|
|
{ dtype_sql_time, "SQL time" },
|
|
{ dtype_blob, "blob" },
|
|
{ dtype_int64, "big integer" },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
|
|
int MOVQ_compare(const dsc* arg1, const dsc* arg2)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M O V Q _ c o m p a r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Compare two descriptors. Return (-1, 0, 1) if a<b, a=b, or a>b.
|
|
*
|
|
**************************************/
|
|
|
|
// Handle the simple (matched) ones first
|
|
|
|
if (arg1->dsc_dtype == arg2->dsc_dtype && arg1->dsc_scale == arg2->dsc_scale)
|
|
{
|
|
const UCHAR* p1 = arg1->dsc_address;
|
|
const UCHAR* p2 = arg2->dsc_address;
|
|
|
|
switch (arg1->dsc_dtype)
|
|
{
|
|
case dtype_short:
|
|
if (*(SSHORT*) p1 == *(SSHORT*) p2)
|
|
return 0;
|
|
if (*(SSHORT*) p1 > *(SSHORT*) p2)
|
|
return 1;
|
|
return -1;
|
|
|
|
case dtype_long:
|
|
case dtype_sql_date:
|
|
case dtype_sql_time:
|
|
if (*(SLONG*) p1 == *(SLONG*) p2)
|
|
return 0;
|
|
if (*(SLONG*) p1 > *(SLONG*) p2)
|
|
return 1;
|
|
return -1;
|
|
case dtype_int64:
|
|
case dtype_timestamp:
|
|
case dtype_quad:
|
|
if (((SLONG*) p1)[0] < ((SLONG*) p2)[0])
|
|
return -1;
|
|
if (((SLONG*) p1)[0] > ((SLONG*) p2)[0])
|
|
return 1;
|
|
if (((ULONG*) p1)[1] < ((ULONG*) p2)[1])
|
|
return -1;
|
|
if (((ULONG*) p1)[1] > ((ULONG*) p2)[1])
|
|
return 1;
|
|
return 0;
|
|
|
|
case dtype_real:
|
|
if (*(float*) p1 == *(float*) p2)
|
|
return 0;
|
|
if (*(float*) p1 > *(float*) p2)
|
|
return 1;
|
|
return -1;
|
|
|
|
case dtype_double:
|
|
if (*(double*) p1 == *(double*) p2)
|
|
return 0;
|
|
if (*(double*) p1 > *(double*) p2)
|
|
return 1;
|
|
return -1;
|
|
|
|
case dtype_text:
|
|
{
|
|
SSHORT length;
|
|
if (arg1->dsc_length >= arg2->dsc_length)
|
|
{
|
|
if (length = arg2->dsc_length)
|
|
do {
|
|
if (*p1++ != *p2++)
|
|
return (p1[-1] > p2[-1]) ? 1 : -1;
|
|
} while (--length);
|
|
if (length = arg1->dsc_length - arg2->dsc_length)
|
|
do {
|
|
if (*p1++ != ' ')
|
|
return (p1[-1] > ' ') ? 1 : -1;
|
|
} while (--length);
|
|
return 0;
|
|
}
|
|
if (length = arg1->dsc_length)
|
|
do {
|
|
if (*p1++ != *p2++)
|
|
return (p1[-1] > p2[-1]) ? 1 : -1;
|
|
} while (--length);
|
|
length = arg2->dsc_length - arg1->dsc_length;
|
|
do {
|
|
if (*p2++ != ' ')
|
|
return (' ' > p2[-1]) ? 1 : -1;
|
|
} while (--length);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle mixed string comparisons
|
|
|
|
if (arg1->dsc_dtype <= dtype_varying && arg2->dsc_dtype <= dtype_varying)
|
|
{
|
|
const TEXT* p1;
|
|
const TEXT* p2;
|
|
SSHORT length = MOVQ_get_string(arg1, &p1, 0, 0);
|
|
SSHORT length2 = MOVQ_get_string(arg2, &p2, 0, 0);
|
|
SSHORT fill = length - length2;
|
|
if (length >= length2)
|
|
{
|
|
if (length2)
|
|
do {
|
|
if (*p1++ != *p2++)
|
|
return (p1[-1] > p2[-1]) ? 1 : -1;
|
|
} while (--length2);
|
|
if (fill > 0)
|
|
do {
|
|
if (*p1++ != ' ')
|
|
return (p1[-1] > ' ') ? 1 : -1;
|
|
} while (--fill);
|
|
return 0;
|
|
}
|
|
if (length)
|
|
{
|
|
do {
|
|
if (*p1++ != *p2++)
|
|
return (p1[-1] > p2[-1]) ? 1 : -1;
|
|
} while (--length);
|
|
}
|
|
do {
|
|
if (*p2++ != ' ')
|
|
return (' ' > p2[-1]) ? 1 : -1;
|
|
} while (++fill);
|
|
return 0;
|
|
}
|
|
|
|
// Handle hetergeneous compares
|
|
|
|
if (arg1->dsc_dtype < arg2->dsc_dtype)
|
|
return (-MOVQ_compare(arg2, arg1));
|
|
|
|
dsc desc;
|
|
SLONG date[2];
|
|
|
|
switch (arg1->dsc_dtype)
|
|
{
|
|
case dtype_timestamp:
|
|
desc.dsc_dtype = dtype_timestamp;
|
|
desc.dsc_length = sizeof(date);
|
|
desc.dsc_scale = 0;
|
|
desc.dsc_address = (UCHAR*) date;
|
|
desc.dsc_sub_type = 0;
|
|
MOVQ_move(arg2, &desc);
|
|
return MOVQ_compare(arg1, &desc);
|
|
|
|
case dtype_sql_time:
|
|
desc.dsc_dtype = dtype_sql_time;
|
|
desc.dsc_length = sizeof(date[0]);
|
|
desc.dsc_scale = 0;
|
|
desc.dsc_address = (UCHAR*) date;
|
|
desc.dsc_sub_type = 0;
|
|
MOVQ_move(arg2, &desc);
|
|
return MOVQ_compare(arg1, &desc);
|
|
|
|
case dtype_sql_date:
|
|
desc.dsc_dtype = dtype_sql_date;
|
|
desc.dsc_length = sizeof(date[0]);
|
|
desc.dsc_scale = 0;
|
|
desc.dsc_address = (UCHAR*) date;
|
|
desc.dsc_sub_type = 0;
|
|
MOVQ_move(arg2, &desc);
|
|
return MOVQ_compare(arg1, &desc);
|
|
|
|
case dtype_short:
|
|
case dtype_long:
|
|
{
|
|
const SSHORT scale = MIN(arg1->dsc_scale, arg2->dsc_scale);
|
|
|
|
const SLONG temp1 = MOVQ_get_long(arg1, scale);
|
|
const SLONG temp2 = MOVQ_get_long(arg2, scale);
|
|
if (temp1 == temp2)
|
|
return 0;
|
|
if (temp1 > temp2)
|
|
return 1;
|
|
return -1;
|
|
}
|
|
|
|
case dtype_real:
|
|
{
|
|
const float temp1 = MOVQ_get_double(arg1);
|
|
const float temp2 = MOVQ_get_double(arg2);
|
|
if (temp1 == temp2)
|
|
return 0;
|
|
if (temp1 > temp2)
|
|
return 1;
|
|
return -1;
|
|
}
|
|
|
|
case dtype_double:
|
|
{
|
|
const double temp1 = MOVQ_get_double(arg1);
|
|
const double temp2 = MOVQ_get_double(arg2);
|
|
if (temp1 == temp2)
|
|
return 0;
|
|
if (temp1 > temp2)
|
|
return 1;
|
|
return -1;
|
|
}
|
|
|
|
case dtype_blob:
|
|
IBERROR(48); // Msg 48 Blob conversion is not supported
|
|
|
|
default:
|
|
mover_error(410, arg1->dsc_dtype, arg2->dsc_dtype);
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
double MOVQ_date_to_double(const dsc* desc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M O V Q _ d a t e _ t o _ d o u b l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert a date to double precision for
|
|
* date arithmetic routines.
|
|
*
|
|
**************************************/
|
|
SLONG temp[2], *date;
|
|
|
|
// If the input descriptor is not in date form, convert it.
|
|
|
|
if (desc->dsc_dtype == dtype_timestamp)
|
|
date = (SLONG *) desc->dsc_address;
|
|
else
|
|
{
|
|
dsc temp_desc;
|
|
temp_desc.dsc_dtype = dtype_timestamp;
|
|
temp_desc.dsc_length = sizeof(temp);
|
|
temp_desc.dsc_scale = 0;
|
|
temp_desc.dsc_sub_type = 0;
|
|
date = temp;
|
|
temp_desc.dsc_address = (UCHAR*) date;
|
|
QLI_validate_desc(temp_desc);
|
|
MOVQ_move(desc, &temp_desc);
|
|
}
|
|
|
|
return date[0] + (double) date[1] / (24. * 60. * 60. * PRECISION);
|
|
}
|
|
|
|
|
|
int MOVQ_decompose(const TEXT* string, USHORT length, SLONG* return_value)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M O V Q _ d e c o m p o s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Decompose a numeric string in mantissa and exponent.
|
|
*
|
|
**************************************/
|
|
SSHORT scale = 0;
|
|
SLONG value = 0;
|
|
SCHAR temp[128];
|
|
|
|
bool sign = false;
|
|
bool fraction = false;
|
|
|
|
const TEXT* p = string;
|
|
const TEXT* const end = p + length;
|
|
for (; p < end; p++)
|
|
{
|
|
if (*p == ',')
|
|
continue;
|
|
|
|
if (DIGIT(*p))
|
|
{
|
|
value = value * 10 + *p - '0';
|
|
if (fraction)
|
|
--scale;
|
|
}
|
|
else if (*p == '.')
|
|
{
|
|
if (fraction)
|
|
{
|
|
MOVQ_terminate(string, temp, length, sizeof(temp));
|
|
ERRQ_error(411, temp);
|
|
}
|
|
else
|
|
fraction = true;
|
|
}
|
|
else if (*p == '-' && !value && !sign)
|
|
sign = true;
|
|
else if (*p == '+' && !value && !sign)
|
|
sign = false;
|
|
else if (*p == 'e' || *p == 'E')
|
|
break;
|
|
else if (*p != ' ')
|
|
{
|
|
MOVQ_terminate(string, temp, length, sizeof(temp));
|
|
ERRQ_error(411, temp);
|
|
}
|
|
}
|
|
|
|
if (sign)
|
|
value = -value;
|
|
|
|
// If there's still something left, there must be an explicit exponent
|
|
|
|
if (p < end)
|
|
{
|
|
SSHORT exp = 0;
|
|
sign = false;
|
|
for (p++; p < end; p++)
|
|
{
|
|
if (DIGIT(*p))
|
|
exp = exp * 10 + *p - '0';
|
|
else if (*p == '-' && !exp)
|
|
sign = true;
|
|
else if (*p == '+' && !exp && !sign)
|
|
continue;
|
|
else if (*p != ' ')
|
|
{
|
|
MOVQ_terminate(string, temp, length, sizeof(temp));
|
|
ERRQ_error(411, temp);
|
|
}
|
|
}
|
|
if (sign)
|
|
scale -= exp;
|
|
else
|
|
scale += exp;
|
|
}
|
|
|
|
*return_value = value;
|
|
|
|
return scale;
|
|
}
|
|
|
|
|
|
void MOVQ_double_to_date( double real, SLONG fixed[2])
|
|
{
|
|
/**************************************
|
|
*
|
|
* M O V Q _ d o u b l e _ t o _ d a t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert a double precision representation of a date
|
|
* to a fixed point representation. Double is used for
|
|
* date arithmetic.
|
|
*
|
|
**************************************/
|
|
|
|
fixed[0] = static_cast<SLONG>(real);
|
|
fixed[1] = static_cast<SLONG>((real - fixed[0]) * 24. * 60. * 60. * PRECISION);
|
|
}
|
|
|
|
|
|
double MOVQ_get_double(const dsc* desc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M O V Q _ g e t _ d o u b l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert something arbitrary to a
|
|
* double_precision representation.
|
|
*
|
|
**************************************/
|
|
double value = 0;
|
|
|
|
switch (desc->dsc_dtype)
|
|
{
|
|
case dtype_short:
|
|
value = *((SSHORT*) desc->dsc_address);
|
|
break;
|
|
|
|
case dtype_long:
|
|
value = *((SLONG*) desc->dsc_address);
|
|
break;
|
|
|
|
case dtype_int64:
|
|
value = *((SINT64*) desc->dsc_address);
|
|
break;
|
|
|
|
case dtype_real:
|
|
return *((float*) desc->dsc_address);
|
|
|
|
case dtype_double:
|
|
return *((double*) desc->dsc_address);
|
|
|
|
case dtype_varying:
|
|
case dtype_cstring:
|
|
case dtype_text:
|
|
return double_from_text(desc);
|
|
|
|
default:
|
|
mover_error(410, desc->dsc_dtype, dtype_double);
|
|
}
|
|
|
|
// Last, but not least, adjust for scale
|
|
|
|
int scale = desc->dsc_scale;
|
|
if (scale == 0)
|
|
return value;
|
|
|
|
if (scale > 0)
|
|
do {
|
|
value *= 10.;
|
|
} while (--scale);
|
|
else
|
|
do {
|
|
value /= 10.;
|
|
} while (++scale);
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
SLONG MOVQ_get_long(const dsc* desc, SSHORT scale)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M O V Q _ g e t _ l o n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert something arbitrary to a long (32 bit) integer of given
|
|
* scale.
|
|
*
|
|
**************************************/
|
|
SLONG value;
|
|
double d;
|
|
|
|
scale -= (SSHORT) desc->dsc_scale;
|
|
|
|
const TEXT* p = (TEXT*) desc->dsc_address;
|
|
switch (desc->dsc_dtype)
|
|
{
|
|
case dtype_short:
|
|
value = *((SSHORT*) p);
|
|
break;
|
|
|
|
case dtype_int64:
|
|
value = *((SINT64*) p);
|
|
break;
|
|
|
|
case dtype_long:
|
|
value = *((SLONG*) p);
|
|
break;
|
|
|
|
case dtype_real:
|
|
d = *((float*) p);
|
|
if (scale > 0)
|
|
do {
|
|
d /= 10.;
|
|
} while (--scale);
|
|
else if (scale < 0)
|
|
do {
|
|
d *= 10.;
|
|
} while (++scale);
|
|
if (d > 0)
|
|
d += 0.5;
|
|
else
|
|
d -= 0.5;
|
|
return (SLONG) d;
|
|
|
|
case dtype_double:
|
|
d = *((double*) p);
|
|
if (scale > 0)
|
|
do {
|
|
d /= 10.;
|
|
} while (--scale);
|
|
else if (scale < 0)
|
|
do {
|
|
d *= 10.;
|
|
} while (++scale);
|
|
if (d > 0)
|
|
d += 0.5;
|
|
else
|
|
d -= 0.5;
|
|
return (SLONG) d;
|
|
|
|
case dtype_varying:
|
|
case dtype_cstring:
|
|
case dtype_text:
|
|
{
|
|
const SSHORT length = MOVQ_get_string(desc, &p, 0, 0);
|
|
scale -= MOVQ_decompose(p, length, &value);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
mover_error(410, desc->dsc_dtype, dtype_long);
|
|
}
|
|
|
|
// Last, but not least, adjust for scale
|
|
|
|
if (scale == 0)
|
|
return value;
|
|
|
|
if (scale > 0)
|
|
{
|
|
if ((desc->dsc_dtype == dtype_short) || (desc->dsc_dtype == dtype_long))
|
|
{
|
|
int fraction = 0;
|
|
do {
|
|
if (scale == 1)
|
|
fraction = value % 10;
|
|
value /= 10;
|
|
} while (--scale);
|
|
if (fraction > 4)
|
|
value++;
|
|
}
|
|
else
|
|
do {
|
|
value /= 10;
|
|
} while (--scale);
|
|
}
|
|
else
|
|
do {
|
|
value *= 10;
|
|
} while (++scale);
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
int MOVQ_get_string(const dsc* desc, const TEXT** address, vary* temp, USHORT length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M O V Q _ g e t _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get address and length of string, converting the value to
|
|
* string, if necessary. The caller must provide a sufficiently
|
|
* large temporary. The address of the resultant string is returned
|
|
* by reference. Get_string returns the length of the string.
|
|
*
|
|
* Note: If the descriptor is known to be a string type, the third
|
|
* argument (temp buffer) may be omitted.
|
|
*
|
|
**************************************/
|
|
|
|
// If the value is already a string (fixed or varying), just return
|
|
// the address and length.
|
|
|
|
if (desc->dsc_dtype == dtype_text)
|
|
{
|
|
*address = (TEXT*) desc->dsc_address;
|
|
return desc->dsc_length;
|
|
}
|
|
|
|
// Perhaps it a "C" type string?
|
|
|
|
if (desc->dsc_dtype == dtype_cstring)
|
|
{
|
|
*address = (TEXT*) desc->dsc_address;
|
|
return MIN(static_cast<int>(strlen((char*)desc->dsc_address)), desc->dsc_length - 1);
|
|
}
|
|
|
|
// No luck -- convert value to varying string.
|
|
|
|
if (desc->dsc_dtype == dtype_varying)
|
|
{
|
|
vary* varying = (vary*) desc->dsc_address;
|
|
*address = varying->vary_string;
|
|
return varying->vary_length;
|
|
}
|
|
|
|
dsc temp_desc;
|
|
temp_desc.dsc_length = length;
|
|
temp_desc.dsc_address = (UCHAR*) temp;
|
|
temp_desc.dsc_scale = 0;
|
|
temp_desc.dsc_dtype = dtype_varying;
|
|
temp_desc.dsc_sub_type = ttype_ascii;
|
|
QLI_validate_desc(temp_desc);
|
|
MOVQ_move(desc, &temp_desc);
|
|
*address = temp->vary_string;
|
|
|
|
return temp->vary_length;
|
|
}
|
|
|
|
|
|
void MOVQ_move(const dsc* from, dsc* to)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M O V Q _ m o v e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Move (and possible convert) something to something else.
|
|
*
|
|
**************************************/
|
|
USHORT length = from->dsc_length;
|
|
UCHAR* p = to->dsc_address;
|
|
const UCHAR* q = from->dsc_address;
|
|
|
|
// If the datatypes and lengths are identical, just move the
|
|
// stuff byte by byte. Although this may seem slower than
|
|
// optimal, it would cost more to find the fast move than the
|
|
// fast move would gain.
|
|
|
|
// if (((ALT_DSC*) from)->dsc_combined_type == ((ALT_DSC*) to)->dsc_combined_type)
|
|
if (DSC_EQUIV(from, to, false))
|
|
{
|
|
if (length)
|
|
memcpy(p, q, length);
|
|
|
|
return;
|
|
}
|
|
|
|
const TEXT* ptr;
|
|
|
|
// Do data type by data type conversions. Not all are supported,
|
|
// and some will drop out for additional handling.
|
|
|
|
switch (to->dsc_dtype)
|
|
{
|
|
case dtype_timestamp:
|
|
switch (from->dsc_dtype)
|
|
{
|
|
case dtype_varying:
|
|
case dtype_cstring:
|
|
case dtype_text:
|
|
length = MOVQ_get_string(from, &ptr, 0, 0);
|
|
string_to_date((TEXT*) ptr, length, (SLONG*) to->dsc_address);
|
|
return;
|
|
case dtype_sql_date:
|
|
((SLONG*) to->dsc_address)[0] = *(SLONG*) from->dsc_address;
|
|
((SLONG*) to->dsc_address)[1] = 0;
|
|
return;
|
|
case dtype_sql_time:
|
|
((SLONG*) to->dsc_address)[0] = 0;
|
|
((SLONG*) to->dsc_address)[1] = *(SLONG*) from->dsc_address;
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case dtype_sql_date:
|
|
switch (from->dsc_dtype)
|
|
{
|
|
case dtype_varying:
|
|
case dtype_cstring:
|
|
case dtype_text:
|
|
{
|
|
SLONG date[2];
|
|
length = MOVQ_get_string(from, &ptr, 0, 0);
|
|
string_to_date((TEXT*) ptr, length, (SLONG*) date);
|
|
((SLONG*) to->dsc_address)[0] = date[0];
|
|
}
|
|
return;
|
|
case dtype_timestamp:
|
|
((SLONG*) to->dsc_address)[0] = ((SLONG*) from->dsc_address)[0];
|
|
return;
|
|
case dtype_sql_time:
|
|
// Error situation
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case dtype_sql_time:
|
|
switch (from->dsc_dtype)
|
|
{
|
|
case dtype_varying:
|
|
case dtype_cstring:
|
|
case dtype_text:
|
|
{
|
|
SLONG date[2];
|
|
length = MOVQ_get_string(from, &ptr, 0, 0);
|
|
string_to_time((TEXT*) ptr, length, (SLONG*) date);
|
|
((SLONG*) to->dsc_address)[0] = date[1];
|
|
}
|
|
return;
|
|
case dtype_timestamp:
|
|
((SLONG*) to->dsc_address)[0] = ((SLONG*) from->dsc_address)[1];
|
|
return;
|
|
case dtype_sql_date:
|
|
// Error situation
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case dtype_text:
|
|
case dtype_cstring:
|
|
case dtype_varying:
|
|
switch (from->dsc_dtype)
|
|
{
|
|
case dtype_varying:
|
|
case dtype_cstring:
|
|
case dtype_text:
|
|
{
|
|
length = MOVQ_get_string(from, &ptr, 0, 0);
|
|
const TEXT* s = ptr;
|
|
|
|
switch (to->dsc_dtype)
|
|
{
|
|
case dtype_text:
|
|
{
|
|
length = MIN(length, to->dsc_length);
|
|
SSHORT fill = to->dsc_length - length;
|
|
if (length)
|
|
memcpy(p, s, length);
|
|
if (fill > 0)
|
|
memset(p + length, ' ', fill);
|
|
return;
|
|
}
|
|
|
|
case dtype_cstring:
|
|
length = MIN(length, to->dsc_length - 1);
|
|
if (length)
|
|
memcpy(p, s, length);
|
|
p[length] = 0;
|
|
return;
|
|
|
|
case dtype_varying:
|
|
{
|
|
length = MIN(length, to->dsc_length - sizeof(SSHORT));
|
|
vary* avary = reinterpret_cast<vary*>(p);
|
|
avary->vary_length = length;
|
|
if (length)
|
|
memcpy(avary->vary_string, s, length);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
case dtype_int64:
|
|
case dtype_short:
|
|
case dtype_long:
|
|
case dtype_real:
|
|
case dtype_double:
|
|
numeric_to_text(from, to);
|
|
return;
|
|
|
|
case dtype_sql_date:
|
|
sql_date_to_text((SLONG*) from->dsc_address, to);
|
|
return;
|
|
case dtype_sql_time:
|
|
sql_time_to_text((ULONG*) from->dsc_address, to);
|
|
return;
|
|
case dtype_timestamp:
|
|
timestamp_to_text((SLONG*) from->dsc_address, to);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case dtype_blob:
|
|
if (from->dsc_dtype == dtype_quad)
|
|
{
|
|
((SLONG*) p)[0] = ((SLONG*) q)[0];
|
|
((SLONG*) p)[1] = ((SLONG*) q)[1];
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case dtype_quad:
|
|
if (from->dsc_dtype == dtype_blob)
|
|
{
|
|
((SLONG*) p)[0] = ((SLONG*) q)[0];
|
|
((SLONG*) p)[1] = ((SLONG*) q)[1];
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case dtype_short:
|
|
{
|
|
const SLONG l = MOVQ_get_long(from, to->dsc_scale);
|
|
*(SSHORT*) p = l;
|
|
if (*(SSHORT*) p != l)
|
|
IBERROR(14); // Msg14 integer overflow
|
|
return;
|
|
}
|
|
|
|
case dtype_long:
|
|
*(SLONG*) p = MOVQ_get_long(from, to->dsc_scale);
|
|
return;
|
|
|
|
case dtype_int64:
|
|
*(SINT64*) p = MOVQ_get_long(from, to->dsc_scale);
|
|
return;
|
|
|
|
case dtype_real:
|
|
*(float*) p = MOVQ_get_double(from);
|
|
return;
|
|
|
|
case dtype_double:
|
|
*(double*) p = MOVQ_get_double(from);
|
|
return;
|
|
}
|
|
|
|
if (to->dsc_dtype == dtype_blob || from->dsc_dtype == dtype_blob)
|
|
IBERROR(55); // Msg 55 Blob conversion is not supported
|
|
|
|
mover_error(410, from->dsc_dtype, to->dsc_dtype);
|
|
}
|
|
|
|
|
|
void MOVQ_terminate(const SCHAR* from, SCHAR* to, USHORT length, USHORT max_length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M O V Q _ t e r m i n a t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Null-terminate a possibly non-
|
|
* null-terminated string with max
|
|
* buffer room.
|
|
*
|
|
**************************************/
|
|
|
|
fb_assert(max_length != 0);
|
|
if (length)
|
|
{
|
|
length = MIN(length, max_length - 1);
|
|
memcpy(to, from, length);
|
|
to[length] = '\0';
|
|
}
|
|
else
|
|
fb_utils::copy_terminate(to, from, max_length);
|
|
}
|
|
|
|
|
|
static void date_error(const TEXT* string, const USHORT length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* d a t e _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* A date conversion error occurred. Complain.
|
|
*
|
|
**************************************/
|
|
SCHAR temp[128];
|
|
|
|
MOVQ_terminate(string, temp, length, sizeof(temp));
|
|
ERRQ_error(56, temp);
|
|
// Msg 56 Error converting string \"%s\" to date
|
|
}
|
|
|
|
|
|
static double double_from_text(const dsc* desc)
|
|
{
|
|
const TEXT* p;
|
|
const SSHORT length = MOVQ_get_string(desc, &p, 0, 0);
|
|
int scale = 0;
|
|
bool fraction = false, sign = false;
|
|
double value = 0;
|
|
const TEXT* const end = p + length;
|
|
for (; p < end; p++)
|
|
{
|
|
if (*p == ',')
|
|
continue;
|
|
|
|
if (DIGIT(*p))
|
|
{
|
|
value = value * 10. + (*p - '0');
|
|
if (fraction)
|
|
scale++;
|
|
}
|
|
else if (*p == '.')
|
|
{
|
|
if (fraction)
|
|
IBERROR(52); // Msg 52 conversion error
|
|
else
|
|
fraction = true;
|
|
}
|
|
else if (!value && *p == '-')
|
|
sign = true;
|
|
else if (!value && *p == '+')
|
|
continue;
|
|
else if (*p == 'e' || *p == 'E')
|
|
break;
|
|
else if (*p != ' ')
|
|
IBERROR(53); // Msg 53 conversion error
|
|
}
|
|
|
|
if (sign)
|
|
value = -value;
|
|
|
|
// If there's still something left, there must be an explicit exponent
|
|
|
|
if (p < end)
|
|
{
|
|
sign = false;
|
|
int exp = 0;
|
|
for (p++; p < end; p++)
|
|
{
|
|
if (DIGIT(*p))
|
|
exp = exp * 10 + *p - '0';
|
|
else if (*p == '-' && !exp)
|
|
sign = true;
|
|
else if (*p == '+' && !exp)
|
|
;
|
|
else if (*p != ' ')
|
|
IBERROR(54); // Msg 54 conversion error
|
|
}
|
|
if (sign)
|
|
scale += exp;
|
|
else
|
|
scale -= exp;
|
|
}
|
|
|
|
if (scale > 0)
|
|
do {
|
|
value /= 10.;
|
|
} while (--scale);
|
|
else if (scale)
|
|
do {
|
|
value *= 10.;
|
|
} while (++scale);
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
static void sql_date_to_text( const SLONG date[1], DSC* to)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s q l _ d a t e _ t o _ t e x t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert date to text.
|
|
*
|
|
**************************************/
|
|
tm times;
|
|
SLONG date2[2];
|
|
|
|
date2[0] = date[0];
|
|
date2[1] = 0;
|
|
isc_decode_date((const ISC_QUAD*) date2, ×);
|
|
|
|
TEXT temp[35];
|
|
sprintf(temp, "%2d-%.3s-%04d", times.tm_mday,
|
|
FB_LONG_MONTHS_UPPER[times.tm_mon], times.tm_year + 1900);
|
|
|
|
const TEXT* p = temp;
|
|
while (*p)
|
|
++p;
|
|
|
|
dsc desc;
|
|
desc.dsc_length = p - temp;
|
|
desc.dsc_address = (UCHAR*) temp;
|
|
desc.dsc_dtype = dtype_text;
|
|
desc.dsc_scale = 0;
|
|
desc.dsc_sub_type = ttype_ascii;
|
|
QLI_validate_desc(desc);
|
|
|
|
MOVQ_move(&desc, to);
|
|
}
|
|
|
|
|
|
static void sql_time_to_text( const ULONG date[1], DSC* to)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s q l _ t i m e _ t o _ t e x t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert sql time to text.
|
|
*
|
|
**************************************/
|
|
tm times;
|
|
SLONG date2[2];
|
|
|
|
date2[0] = 0;
|
|
date2[1] = date[0];
|
|
|
|
isc_decode_date((const ISC_QUAD*) date2, ×);
|
|
|
|
TEXT temp[35];
|
|
sprintf(temp, " %2d:%.2d:%.2d.%.4" SLONGFORMAT, times.tm_hour, times.tm_min,
|
|
times.tm_sec, date2[1] % PRECISION);
|
|
|
|
const TEXT* p = temp;
|
|
while (*p)
|
|
++p;
|
|
|
|
dsc desc;
|
|
desc.dsc_length = p - temp;
|
|
desc.dsc_address = (UCHAR*) temp;
|
|
desc.dsc_dtype = dtype_text;
|
|
desc.dsc_scale = 0;
|
|
desc.dsc_sub_type = ttype_ascii;
|
|
QLI_validate_desc(desc);
|
|
|
|
MOVQ_move(&desc, to);
|
|
}
|
|
|
|
|
|
static void timestamp_to_text( const SLONG date[2], DSC* to)
|
|
{
|
|
/**************************************
|
|
*
|
|
* t i m e s t a m p _ t o _ t e x t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert date to text.
|
|
*
|
|
**************************************/
|
|
tm times;
|
|
isc_decode_date((const ISC_QUAD*) date, ×);
|
|
|
|
TEXT temp[35];
|
|
sprintf(temp, "%2d-%.3s-%04d", times.tm_mday,
|
|
FB_LONG_MONTHS_UPPER[times.tm_mon], times.tm_year + 1900);
|
|
|
|
if (times.tm_hour || times.tm_min || times.tm_sec || date[1])
|
|
{
|
|
TEXT time[15];
|
|
sprintf(time, " %2d:%.2d:%.2d.%.4" SLONGFORMAT, times.tm_hour, times.tm_min,
|
|
times.tm_sec, date[1] % PRECISION);
|
|
strcat(temp, time);
|
|
}
|
|
|
|
const TEXT* p = temp;
|
|
while (*p)
|
|
++p;
|
|
|
|
dsc desc;
|
|
desc.dsc_length = p - temp;
|
|
desc.dsc_address = (UCHAR*) temp;
|
|
desc.dsc_dtype = dtype_text;
|
|
desc.dsc_scale = 0;
|
|
desc.dsc_sub_type = ttype_ascii;
|
|
QLI_validate_desc(desc);
|
|
|
|
MOVQ_move(&desc, to);
|
|
}
|
|
|
|
|
|
static void mover_error( int pattern, USHORT in_type, USHORT out_type)
|
|
{
|
|
/**************************************
|
|
*
|
|
* m o v e r _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Return a reasonable error for
|
|
* unreasonable conversions and
|
|
* comparisons.
|
|
*
|
|
**************************************/
|
|
TEXT in_name[25], out_name[25], msg_unknown[40];
|
|
|
|
ERRQ_msg_get(504, msg_unknown, sizeof(msg_unknown)); // Msg504 unknown datatype %d
|
|
|
|
const TEXT* in = type_name(in_type);
|
|
if (!in)
|
|
{
|
|
in = in_name;
|
|
sprintf(in_name, msg_unknown, in_type);
|
|
}
|
|
|
|
const TEXT* out = type_name(out_type);
|
|
if (!out)
|
|
{
|
|
out = out_name;
|
|
sprintf(out_name, msg_unknown, out_type);
|
|
}
|
|
|
|
ERRQ_error(pattern, SafeArg() << in << out);
|
|
}
|
|
|
|
|
|
static void now_to_date(const tm* time, SLONG date[2])
|
|
{
|
|
/**************************************
|
|
*
|
|
* n o w _ t o _ d a t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert "now" (or best guess) to
|
|
* a date attempting to get millisecond
|
|
* precision. This unfortunately means
|
|
* that we use one routine for VAX, one
|
|
* for Apollo, and a third for Unix.
|
|
*
|
|
**************************************/
|
|
|
|
isc_encode_date(time, (ISC_QUAD*)date);
|
|
}
|
|
|
|
|
|
static void numeric_to_text(const dsc* from, dsc* to)
|
|
{
|
|
/**************************************
|
|
*
|
|
* n u m e r i c _ t o _ t e x t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert your basic number to nice, formatted text.
|
|
*
|
|
**************************************/
|
|
|
|
// Save (or compute) scale of source. Then convert source to ordinary longword.
|
|
|
|
SSHORT scale = from->dsc_scale;
|
|
|
|
SSHORT pad = 0, decimal = 0;
|
|
if (scale > 0)
|
|
pad = scale;
|
|
else if (scale < 0)
|
|
decimal = 1;
|
|
|
|
SLONG n;
|
|
dsc intermediate;
|
|
intermediate.dsc_dtype = dtype_long;
|
|
intermediate.dsc_length = sizeof(SLONG);
|
|
intermediate.dsc_scale = scale;
|
|
intermediate.dsc_sub_type = 0;
|
|
intermediate.dsc_address = (UCHAR*) &n;
|
|
QLI_validate_desc(intermediate);
|
|
|
|
MOVQ_move(from, &intermediate);
|
|
|
|
// Check for negation, then convert the number to a string of digits
|
|
|
|
SSHORT neg = 0;
|
|
if (n < 0)
|
|
{
|
|
neg = 1;
|
|
n = -n;
|
|
}
|
|
|
|
TEXT temp[32];
|
|
TEXT* p = temp;
|
|
|
|
do {
|
|
*p++ = n % 10 + '0';
|
|
n /= 10;
|
|
} while (n);
|
|
|
|
// Compute the total length off the field formatted. Make sure it
|
|
// fits. Keep in mind that routine handles both string and varying
|
|
// string fields.
|
|
|
|
SSHORT l = p - temp;
|
|
const FB_SIZE_T length = l + neg + decimal + pad;
|
|
|
|
if ((to->dsc_dtype == dtype_text && length > to->dsc_length) ||
|
|
(to->dsc_dtype == dtype_cstring && length >= to->dsc_length) ||
|
|
(to->dsc_dtype == dtype_varying && length > to->dsc_length - sizeof(SSHORT)))
|
|
{
|
|
IBERROR(57); // Msg 57 overflow during conversion
|
|
}
|
|
|
|
// Hopefully a cstring never reached this point.
|
|
TEXT* q = (TEXT*) ((to->dsc_dtype == dtype_text) ?
|
|
to->dsc_address : to->dsc_address + sizeof(SSHORT));
|
|
|
|
// If negative, put in minus sign
|
|
|
|
if (neg)
|
|
*q++ = '-';
|
|
|
|
// If a decimal point is required, do the formatting. Otherwise just
|
|
// copy number
|
|
|
|
if (scale < 0)
|
|
{
|
|
if ((l += scale) > 0)
|
|
do {
|
|
*q++ = *--p;
|
|
} while (--l);
|
|
*q++ = '.';
|
|
do {
|
|
*q++ = *--p;
|
|
} while (++scale);
|
|
}
|
|
else
|
|
do {
|
|
*q++ = *--p;
|
|
} while (--l);
|
|
|
|
// If padding is required, do it now.
|
|
|
|
if (pad)
|
|
do {
|
|
*q++ = '0';
|
|
} while (--pad);
|
|
|
|
// Finish up by padding (if fixed) or computing the actual length
|
|
// (varying string)
|
|
|
|
if (to->dsc_dtype == dtype_text)
|
|
{
|
|
if (l = to->dsc_length - length)
|
|
do {
|
|
*q++ = ' ';
|
|
} while (--l);
|
|
return;
|
|
}
|
|
|
|
if (to->dsc_dtype == dtype_cstring)
|
|
{
|
|
*q = 0;
|
|
return;
|
|
}
|
|
|
|
*(SSHORT*) (to->dsc_address) = static_cast<SSHORT>((UCHAR*) q - to->dsc_address - sizeof(SSHORT));
|
|
}
|
|
|
|
|
|
static void string_to_date(const TEXT* string, USHORT length, SLONG date[2])
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t r i n g _ t o _ d a t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert an arbitrary string to a date.
|
|
*
|
|
**************************************/
|
|
if (!length)
|
|
{
|
|
date[0] = date[1] = 0;
|
|
return;
|
|
}
|
|
|
|
const TEXT* p = string;
|
|
const TEXT* const end = p + length;
|
|
|
|
USHORT month_position = 0;
|
|
|
|
const time_t clock = time(0);
|
|
tm* today = localtime(&clock);
|
|
|
|
USHORT components[7];
|
|
for (int i = 0; i < 7; i++)
|
|
components[i] = 0;
|
|
|
|
// Parse components
|
|
|
|
TEXT temp[15];
|
|
USHORT n, precision;
|
|
bool year = false;
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
{
|
|
|
|
// Skip leading blanks. If we run out of characters, we're done with parse.
|
|
|
|
while (p < end && *p == ' ')
|
|
p++;
|
|
if (p == end)
|
|
break;
|
|
|
|
// Handle digit or character strings
|
|
|
|
TEXT c = UPPER(*p);
|
|
if (DIGIT(c))
|
|
{
|
|
precision = n = 0;
|
|
while (p < end && DIGIT(*p))
|
|
{
|
|
n = n * 10 + *p++ - '0';
|
|
precision++;
|
|
}
|
|
if (i == 2)
|
|
year = true;
|
|
}
|
|
else if (LETTER(c))
|
|
{
|
|
TEXT* t = temp;
|
|
while (p < end && LETTER(c))
|
|
{
|
|
c = UPPER(*p);
|
|
if (!LETTER(c))
|
|
break;
|
|
*t++ = c;
|
|
p++;
|
|
}
|
|
*t = 0;
|
|
const TEXT* const* month_ptr = FB_LONG_MONTHS_UPPER;
|
|
while (true)
|
|
{
|
|
if (!*month_ptr)
|
|
{
|
|
while (++p < end)
|
|
if (*p != ' ' && *p != '\t' && *p != 0)
|
|
date_error(string, length);
|
|
|
|
if (strcmp(temp, NOW) == 0)
|
|
{
|
|
now_to_date(today, date);
|
|
return;
|
|
}
|
|
today->tm_hour = today->tm_min = today->tm_sec = 0;
|
|
isc_encode_date(today, (ISC_QUAD*)date);
|
|
if (strcmp(temp, TODAY) == 0)
|
|
return;
|
|
if (strcmp(temp, TOMORROW) == 0)
|
|
{
|
|
++date[0];
|
|
return;
|
|
}
|
|
if (strcmp(temp, YESTERDAY) == 0)
|
|
{
|
|
--date[0];
|
|
return;
|
|
}
|
|
date_error(string, length);
|
|
}
|
|
t = temp;
|
|
for (const TEXT* m = *month_ptr++; *t && *t == *m; t++, m++);
|
|
if (!*t)
|
|
break;
|
|
}
|
|
n = month_ptr - FB_LONG_MONTHS_UPPER;
|
|
month_position = i;
|
|
}
|
|
else
|
|
{
|
|
date_error(string, length);
|
|
return;
|
|
}
|
|
components[i] = n;
|
|
while (p < end && *p == ' ')
|
|
p++;
|
|
|
|
switch (*p)
|
|
{
|
|
case '.':
|
|
if (!month_position && i < 2)
|
|
month_position = 1;
|
|
// fall into
|
|
case '/':
|
|
case '-':
|
|
case ',':
|
|
case ':':
|
|
p++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Slide things into day, month, year form
|
|
|
|
tm times;
|
|
if (month_position)
|
|
{
|
|
times.tm_mon = components[1];
|
|
times.tm_mday = components[0];
|
|
}
|
|
else
|
|
{
|
|
times.tm_mon = components[0];
|
|
times.tm_mday = components[1];
|
|
}
|
|
|
|
// Handle defaulting of year
|
|
|
|
if (((times.tm_year = components[2]) == 0) && !year)
|
|
times.tm_year = today->tm_year + 1900;
|
|
else if (times.tm_year < 100)
|
|
{
|
|
if (times.tm_year < (today->tm_year - 50) % 100)
|
|
times.tm_year += 2000;
|
|
else
|
|
times.tm_year += 1900;
|
|
}
|
|
|
|
times.tm_year -= 1900;
|
|
times.tm_mon -= 1;
|
|
times.tm_hour = components[3];
|
|
times.tm_min = components[4];
|
|
times.tm_sec = components[5];
|
|
|
|
// convert day/month/year to Julian and validate result
|
|
|
|
isc_encode_date(×, (ISC_QUAD*)date);
|
|
tm times2;
|
|
isc_decode_date((ISC_QUAD*)date, ×2);
|
|
|
|
if (times.tm_year != times2.tm_year ||
|
|
times.tm_mon != times2.tm_mon || times.tm_mday != times2.tm_mday)
|
|
{
|
|
date_error(string, length);
|
|
}
|
|
|
|
while (precision++ < 4)
|
|
components[6] *= 10;
|
|
|
|
date[1] += components[6];
|
|
|
|
}
|
|
|
|
|
|
static void string_to_time(const TEXT* string, USHORT length, SLONG date[2])
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t r i n g _ t o _ t i m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert an arbitrary string to a time.
|
|
*
|
|
**************************************/
|
|
if (!length)
|
|
{
|
|
date[0] = date[1] = 0;
|
|
return;
|
|
}
|
|
|
|
const TEXT* p = string;
|
|
const TEXT* const end = p + length;
|
|
|
|
const time_t clock = time(0);
|
|
const tm* today = localtime(&clock);
|
|
|
|
int i;
|
|
USHORT components[7];
|
|
for (i = 0; i < 7; i++)
|
|
components[i] = 0;
|
|
|
|
// Parse components
|
|
|
|
TEXT temp[15];
|
|
USHORT n, precision;
|
|
|
|
for (i = 3; i < 7; i++)
|
|
{
|
|
|
|
// Skip leading blanks. If we run out of characters, we're done with parse.
|
|
|
|
while (p < end && *p == ' ')
|
|
p++;
|
|
if (p == end)
|
|
break;
|
|
|
|
// Handle digit or character strings
|
|
|
|
TEXT c = UPPER(*p);
|
|
if (DIGIT(c))
|
|
{
|
|
precision = n = 0;
|
|
while (p < end && DIGIT(*p))
|
|
{
|
|
n = n * 10 + *p++ - '0';
|
|
precision++;
|
|
}
|
|
}
|
|
else if (LETTER(c))
|
|
{
|
|
TEXT* t = temp;
|
|
while (p < end && LETTER(c))
|
|
{
|
|
c = UPPER(*p);
|
|
if (!LETTER(c))
|
|
break;
|
|
*t++ = c;
|
|
p++;
|
|
}
|
|
*t = 0;
|
|
while (++p < end)
|
|
{
|
|
if (*p != ' ' && *p != '\t' && *p != 0)
|
|
date_error(string, length);
|
|
}
|
|
|
|
if (strcmp(temp, NOW) == 0)
|
|
{
|
|
now_to_date(today, date);
|
|
return;
|
|
}
|
|
date_error(string, length);
|
|
}
|
|
else
|
|
{
|
|
date_error(string, length);
|
|
return;
|
|
}
|
|
components[i] = n;
|
|
while (p < end && *p == ' ')
|
|
p++;
|
|
|
|
if (*p == '/' || *p == '-' || *p == ',' || *p == ':' || *p == '.')
|
|
{
|
|
p++;
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
tm times;
|
|
times.tm_hour = components[3];
|
|
times.tm_min = components[4];
|
|
times.tm_sec = components[5];
|
|
|
|
// convert day/month/year to Julian and validate result
|
|
|
|
isc_encode_date(×, (ISC_QUAD*)date);
|
|
|
|
while (precision++ < 4)
|
|
components[6] *= 10;
|
|
|
|
date[1] += components[6];
|
|
|
|
}
|
|
|
|
|
|
static const TEXT* type_name( USHORT dtype)
|
|
{
|
|
/**************************************
|
|
*
|
|
* t y p e _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Return the name of a data type.
|
|
*
|
|
**************************************/
|
|
for (const dtypes_t* names = dtypes_table; names->description; names++)
|
|
{
|
|
if (names->type == dtype)
|
|
return names->description;
|
|
}
|
|
return NULL;
|
|
}
|