8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 08:03:03 +01:00
firebird-mirror/src/jrd/gds.cpp
2003-11-06 03:03:36 +00:00

3723 lines
86 KiB
C++

/*
* PROGRAM: JRD Access Method
* MODULE: gds.cpp
* DESCRIPTION: User callable routines
*
* 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): ______________________________________.
*
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "IMP" port
* 2002.02.15 Sean Leyne - Code Cleanup, removed obsolete "M88K" port
*
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
*
* 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
* 2003.05.11 Nickolay Samofatov - rework temp stuff
*
*/
#define IO_RETRY 20
// 11 Sept 2002 Nickolay Samofatov
// this defined in included dsc.h
//#define ISC_TIME_SECONDS_PRECISION 10000L
//#define ISC_TIME_SECONDS_PRECISION_SCALE -4
#include "firebird.h"
#include "../jrd/ib_stdio.h"
#include <stdlib.h>
#include <string.h>
#include "../jrd/common.h"
#include "../jrd/gdsassert.h"
#include "../jrd/file_params.h"
#include "../jrd/msg_encode.h"
#include "../jrd/iberr.h"
#include "../jrd/gds_proto.h"
#include "../jrd/os/path_utils.h"
#include "../jrd/misc_proto.h"
#include "../jrd/constants.h"
#include "../common/classes/locks.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifndef WIN_NT
#include <sys/param.h>
#include <errno.h>
#endif
#include <stdarg.h>
#include "../jrd/jrd_time.h"
#include "../jrd/misc.h"
#ifdef DARWIN
#include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFURL.h>
#endif
#if defined(WIN_NT)
#include <io.h>
#include <process.h>
#include <sys/types.h>
#include <sys/timeb.h>
#endif
#ifdef VMS
#include <file.h>
#include <ib_perror.h>
#include <descrip.h>
#include <types.h>
#include <stat.h>
#include <rmsdef.h>
#else /* !VMS */
#include <sys/types.h>
#include <sys/stat.h>
#ifndef WIN_NT
#include <sys/file.h>
#endif
#ifndef O_RDWR
#include <fcntl.h>
#endif
#ifdef UNIX
#if (defined SUPERSERVER) || (defined SOLARIS)
#include <sys/mman.h>
#include <sys/resource.h>
#include "../jrd/err_proto.h"
#endif
#endif
#endif /* VMS */
#include "../common/config/config.h"
// Turn on V4 mutex protection for gds__alloc/free
// 03/23/2003 BRS. Those defines don't do anything, V4_ macros are defined in thd.h
// but this file is included before this definition and so the macros are defined as empty
// Those definitions are commented to allow V4_ macros to be inside V4_THREADING ifdefs
// The include chain is
// gdsassert.h -> gds_proto.h -> fil.h -> thd.h
/*
#ifdef WIN_NT
#define V4_THREADING
#endif
#ifdef SOLARIS_MT
#define V4_THREADING
#endif
#ifdef SUPERSERVER
#define V4_THREADING // RFM: 9/22/2000 fix from Inprise tree,
// Inprise bug 114840
#endif
// The following ifdef was added to build thread safe gds shared
// library on linux platform. It seems the gdslib works now (20020220)
// with thread enabled applications. Anyway, more tests should be
// done as I don't have deep knowledge of the interbase/firebird
// engine and this change may imply side effect I haven't known
// about yet. Tomas Nejedlik (tomas@nejedlik.cz)
#if ((defined(LINUX) || defined(FREEBSD)) && defined(SUPERCLIENT))
#define V4_THREADING
#endif
*/
#ifdef SUPERSERVER
static const TEXT gdslogid[] = " (Server)";
#else
#ifdef SUPERCLIENT
static const TEXT gdslogid[] = " (Client)";
#else
static const TEXT gdslogid[] = "";
#endif
#endif
static const char * FB_PID_FILE = "fb_%d";
#include "gen/sql_code.h"
#include "../jrd/thd.h"
#include "../jrd/gdsold.h"
//#include "../jrd/gen/codes.h" // is already included in gdsold.h
#include "../jrd/blr.h"
#include "../jrd/msg.h"
#include "../jrd/fil.h"
#include "../jrd/gds_proto.h"
#include "../jrd/isc_proto.h"
#ifndef REQUESTER
#include "../jrd/os/isc_i_proto.h"
#endif
#ifdef WIN_NT
#define _WINSOCKAPI_
#include <windows.h>
#include <share.h>
#include "thd_proto.h"
#include "err_proto.h"
#undef leave
#ifdef TEXT
#undef TEXT
#endif
#define TEXT SCHAR
#endif /* WIN_NT */
// Number of times to try to generate new name for temporary file
#define MAX_TMPFILE_TRIES 256
static char *ib_prefix = 0;
static char *ib_prefix_lock = 0;
static char *ib_prefix_msg = 0;
#include "gen/msgs.h"
#ifndef FOPEN_APPEND_TYPE
#define FOPEN_APPEND_TYPE "a"
#endif
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef GENERIC_SQLCODE
#define GENERIC_SQLCODE -999
#endif
static char ib_prefix_val[MAXPATHLEN];
static char ib_prefix_lock_val[MAXPATHLEN];
static char ib_prefix_msg_val[MAXPATHLEN];
#ifndef INCLUDE_FB_TYPES_H
#include "../include/fb_types.h"
#endif
// This structure is used to parse the firebird.msg file.
typedef struct gds_msg
{
ULONG msg_top_tree;
int msg_file;
USHORT msg_bucket_size;
USHORT msg_levels;
SCHAR msg_bucket[1];
} *GDS_MSG;
// CVC: This structure has a totally different layout than "class ctl" from
// blob_filter.h and "struct isc_blob_ctl" from ibase.h. These two should match
// for blob filters to work. However, this one is private to gds.cpp and hence
// I renamed it gds_ctl to avoid confusion and possible name clashes.
// However, filters.cpp calls gds__print_blr(), but this struct is not shared
// between the two modules.
struct gds_ctl
{
const UCHAR* ctl_blr; /* Running blr string */
const UCHAR* ctl_blr_start; /* Original start of blr string */
FPTR_PRINT_CALLBACK ctl_routine; /* Call back */
void* ctl_user_arg; /* User argument */
TEXT* ctl_ptr;
SSHORT ctl_language;
TEXT ctl_buffer[PRETTY_BUFFER_SIZE];
};
#ifdef DEV_BUILD
void GDS_breakpoint(int);
#endif
static void blr_error(gds_ctl*, const TEXT*, ...) ATTRIBUTE_FORMAT(2,3);
static void blr_format(gds_ctl*, const char*, ...) ATTRIBUTE_FORMAT(2,3);
static void blr_indent(gds_ctl*, SSHORT);
static void blr_print_blr(gds_ctl*, UCHAR);
static SCHAR blr_print_byte(gds_ctl*);
static SCHAR blr_print_char(gds_ctl*);
static void blr_print_cond(gds_ctl*);
static int blr_print_dtype(gds_ctl*);
static void blr_print_join(gds_ctl*);
static SLONG blr_print_line(gds_ctl*, SSHORT);
static void blr_print_verb(gds_ctl*, SSHORT);
static int blr_print_word(gds_ctl*);
static void init(void);
static int yday(const tm*);
static void ndate(SLONG, tm*);
static GDS_DATE nday(const tm*);
static void sanitize(TEXT*);
static void safe_concat_path(TEXT* destbuf, const TEXT* srcbuf);
/* Generic cleanup handlers */
typedef struct clean
{
struct clean* clean_next;
void (*clean_routine)(void*);
void* clean_arg;
} *CLEAN;
static CLEAN cleanup_handlers = NULL;
static GDS_MSG default_msg = NULL;
static SLONG initialized = FALSE;
#ifdef DEBUG_GDS_ALLOC
void* API_ROUTINE gds__alloc_debug(SLONG size_request,
TEXT* filename,
ULONG lineno)
{
return getDefaultMemoryPool()->allocate(size_request, 0, filename, lineno);
}
#else
void* API_ROUTINE gds__alloc(SLONG size_request)
{
return getDefaultMemoryPool()->allocate(size_request);
}
#endif
ULONG API_ROUTINE gds__free(void* blk) {
getDefaultMemoryPool()->deallocate(blk);
return 0;
}
#ifdef UNIX
static SLONG gds_pid = 0;
#endif
/* VMS structure to declare exit handler */
#ifdef VMS
static SLONG exit_status = 0;
static struct
{
SLONG link;
int (*exit_handler) ();
SLONG args;
SLONG arg[1];
} exit_description;
#endif
/* BLR Pretty print stuff */
#define PRINT_VERB blr_print_verb (control, level)
#define PRINT_LINE blr_print_line (control, (SSHORT) offset)
#define PRINT_BYTE blr_print_byte (control)
#define PRINT_CHAR blr_print_char (control)
#define PRINT_WORD blr_print_word (control)
#define PRINT_COND blr_print_cond (control)
#define PRINT_DTYPE blr_print_dtype (control)
#define PRINT_JOIN blr_print_join (control)
#define BLR_BYTE *(control->ctl_blr)++
#define PUT_BYTE(byte) *(control->ctl_ptr)++ = byte
#define op_line 1
#define op_verb 2
#define op_byte 3
#define op_word 4
#define op_pad 5
#define op_dtype 6
#define op_message 7
#define op_literal 8
#define op_begin 9
#define op_map 10
#define op_args 11
#define op_union 12
#define op_indent 13
#define op_join 14
#define op_parameters 15
#define op_error_handler 16
#define op_set_error 17
#define op_literals 18
#define op_relation 20
#define op_exec_into 21
static const UCHAR
/* generic print formats */
zero[] = { op_line, 0 },
one[] = { op_line, op_verb, 0},
two[] = { op_line, op_verb, op_verb, 0},
three[] = { op_line, op_verb, op_verb, op_verb, 0},
field[] = { op_byte, op_byte, op_literal, op_pad, op_line, 0},
byte[] = { op_byte, op_line, 0},
byte_args[] = { op_byte, op_line, op_args, 0},
byte_byte[] = { op_byte, op_byte, op_line, 0},
byte_verb[] = { op_byte, op_line, op_verb, 0},
byte_verb_verb[] = { op_byte, op_line, op_verb, op_verb, 0},
byte_literal[] = { op_byte, op_literal, op_line, 0},
byte_byte_verb[] = { op_byte, op_byte, op_line, op_verb, 0},
parm[] = { op_byte, op_word, op_line, 0}, /* also field id */
parm2[] = { op_byte, op_word, op_word, op_line, 0},
parm3[] = { op_byte, op_word, op_word, op_word, op_line, 0},
/* formats specific to a verb */
begin[] = { op_line, op_begin, op_verb, 0},
literal[] = { op_dtype, op_literal, op_line, 0},
message[] = { op_byte, op_word, op_line, op_message, 0},
rse[] = { op_byte, op_line, op_begin, op_verb, 0},
relation[] = { op_byte, op_literal, op_pad, op_byte, op_line, 0},
relation2[] = { op_byte, op_literal, op_line, op_indent, op_byte,
op_literal, op_pad, op_byte, op_line, 0},
aggregate[] = { op_byte, op_line, op_verb, op_verb, op_verb, 0},
rid[] = { op_word, op_byte, op_line, 0},
rid2[] = { op_word, op_byte, op_literal, op_pad, op_byte, op_line, 0},
union_ops[] = { op_byte, op_byte, op_line, op_union, 0},
map[] = { op_word, op_line, op_map, 0},
function[] = { op_byte, op_literal, op_byte, op_line, op_args, 0},
gen_id[] = { op_byte, op_literal, op_line, op_verb, 0},
declare[] = { op_word, op_dtype, op_line, 0},
variable[] = { op_word, op_line, 0},
indx[] = { op_line, op_verb, op_indent, op_byte, op_line, op_args, 0},
find[] = { op_byte, op_verb, op_verb, op_indent, op_byte, op_line, op_args, 0},
seek[] = { op_line, op_verb, op_verb, 0},
join[] = { op_join, op_line, 0},
exec_proc[] = { op_byte, op_literal, op_line, op_indent, op_word, op_line,
op_parameters, op_indent, op_word, op_line, op_parameters, 0},
procedure[] = { op_byte, op_literal, op_pad, op_byte, op_line, op_indent,
op_word, op_line, op_parameters, 0},
pid[] = { op_word, op_pad, op_byte, op_line, op_indent, op_word,
op_line, op_parameters, 0},
error_handler[] = { op_word, op_line, op_error_handler, 0},
set_error[] = { op_set_error, op_line, 0},
cast[] = { op_dtype, op_line, op_verb, 0},
indices[] = { op_byte, op_line, op_literals, 0},
lock_relation[] = { op_line, op_indent, op_relation, op_line, op_verb, 0},
range_relation[] = { op_line, op_verb, op_indent, op_relation, op_line, 0},
extract[] = { op_line, op_byte, op_verb, 0},
user_savepoint[] = { op_byte, op_byte, op_literal, op_line, 0},
exec_into[] = { op_word, op_line, op_indent, op_exec_into, 0};
#include "../jrd/blp.h"
#define FB_ENV "FIREBIRD"
#define FB_LOCK_ENV "FIREBIRD_LOCK"
#define FB_MSG_ENV "FIREBIRD_MSG"
#define FB_TMP_ENV "FIREBIRD_TMP"
#ifdef WIN_NT
#define EXPAND_PATH(relative, absolute) _fullpath(absolute, relative, MAXPATHLEN)
#define COMPARE_PATH(a,b) _stricmp(a,b)
#else
#define EXPAND_PATH(relative, absolute) realpath(relative, absolute)
#define COMPARE_PATH(a,b) strcmp(a,b)
#endif
// This function is very crude, but signal-safe.
// CVC: This function converts and ULONG into a string. I renamed the param
// maxlen to minlen because it represents the minimum length the number will
// be printed in. However, this means buffer should be large enough to contain
// any ULONG (11 bytes) and thus prevent a buffer overflow. If minlen >= 11,
// then buffer should have be of size = (minlen + 1). A small anomaly means
// that when "value" is zero, only filler is stored in buffer: no ACSCII(48)
// appears in buffer, unless filler itself is ASCII(48) == '0'.
void gds__ulstr(char* buffer, ULONG value, const int minlen, const char filler)
{
ULONG n = value;
int c = 0;
while (n) {
n = n / 10;
c++;
}
if (minlen > c)
c = minlen;
char* p = buffer + c;
while (value) {
*--p = '0' + (value % 10);
value = value / 10;
}
while (p != buffer) {
*--p = filler;
}
buffer[c] = 0;
}
ISC_STATUS API_ROUTINE gds__decode(ISC_STATUS code, USHORT* fac, USHORT* class_)
{
/**************************************
*
* g d s _ $ d e c o d e
*
**************************************
*
* Functional description
* Translate a status codes from system dependent form to
* network form.
*
**************************************/
if (!code)
{
return FB_SUCCESS;
}
else if ((code & ISC_MASK) != ISC_MASK)
{
/* not an ISC error message */
return code;
}
*fac = GET_FACILITY(code);
*class_ = GET_CLASS(code);
return GET_CODE(code);
}
void API_ROUTINE isc_decode_date(const GDS_QUAD* date, void* times_arg)
{
/**************************************
*
* i s c _ d e c o d e _ d a t e
*
**************************************
*
* Functional description
* Convert from internal timestamp format to UNIX time structure.
*
* Note: this API is historical - the prefered entrypoint is
* isc_decode_timestamp
*
**************************************/
isc_decode_timestamp((const GDS_TIMESTAMP*) date, times_arg);
}
void API_ROUTINE isc_decode_sql_date(const GDS_DATE* date, void* times_arg)
{
/**************************************
*
* i s c _ d e c o d e _ s q l _ d a t e
*
**************************************
*
* Functional description
* Convert from internal DATE format to UNIX time structure.
*
**************************************/
tm* times = (struct tm*) times_arg;
memset(times, 0, sizeof(*times));
ndate(*date, times);
times->tm_yday = yday(times);
if ((times->tm_wday = (*date + 3) % 7) < 0)
times->tm_wday += 7;
}
void API_ROUTINE isc_decode_sql_time(const GDS_TIME* sql_time, void* times_arg)
{
/**************************************
*
* i s c _ d e c o d e _ s q l _ t i m e
*
**************************************
*
* Functional description
* Convert from internal TIME format to UNIX time structure.
*
**************************************/
tm* times = (struct tm*) times_arg;
memset(times, 0, sizeof(*times));
const ULONG minutes = *sql_time / (ISC_TIME_SECONDS_PRECISION * 60);
times->tm_hour = minutes / 60;
times->tm_min = minutes % 60;
times->tm_sec = (*sql_time / ISC_TIME_SECONDS_PRECISION) % 60;
}
void API_ROUTINE isc_decode_timestamp(const GDS_TIMESTAMP* date, void* times_arg)
{
/**************************************
*
* i s c _ d e c o d e _ t i m e s t a m p
*
**************************************
*
* Functional description
* Convert from internal timestamp format to UNIX time structure.
*
* Note: the date arguement is really an ISC_TIMESTAMP -- however the
* definition of ISC_TIMESTAMP is not available from all the source
* modules that need to use isc_encode_timestamp
*
**************************************/
tm* times = (struct tm*) times_arg;
memset(times, 0, sizeof(*times));
ndate(date->timestamp_date, times);
times->tm_yday = yday(times);
if ((times->tm_wday = (date->timestamp_date + 3) % 7) < 0)
times->tm_wday += 7;
const ULONG minutes = date->timestamp_time / (ISC_TIME_SECONDS_PRECISION * 60);
times->tm_hour = minutes / 60;
times->tm_min = minutes % 60;
times->tm_sec = (date->timestamp_time / ISC_TIME_SECONDS_PRECISION) % 60;
}
ISC_STATUS API_ROUTINE gds__encode(ISC_STATUS code, USHORT facility)
{
/**************************************
*
* g d s _ $ e n c o d e
*
**************************************
*
* Functional description
* Translate a status codes from network format to system
* dependent form.
*
**************************************/
if (!code)
return FB_SUCCESS;
return ENCODE_ISC_MSG(code, facility);
}
void API_ROUTINE isc_encode_date(const void* times_arg, GDS_QUAD* date)
{
/**************************************
*
* i s c _ e n c o d e _ d a t e
*
**************************************
*
* Functional description
* Convert from UNIX time structure to internal timestamp format.
*
* Note: This API is historical -- the prefered entrypoint is
* isc_encode_timestamp
*
**************************************/
isc_encode_timestamp(times_arg, (GDS_TIMESTAMP*) date);
}
void API_ROUTINE isc_encode_sql_date(const void* times_arg, GDS_DATE* date)
{
/**************************************
*
* i s c _ e n c o d e _ s q l _ d a t e
*
**************************************
*
* Functional description
* Convert from UNIX time structure to internal date format.
*
**************************************/
*date = nday((const struct tm*) times_arg);
}
void API_ROUTINE isc_encode_sql_time(const void* times_arg, GDS_TIME* isc_time)
{
/**************************************
*
* i s c _ e n c o d e _ s q l _ t i m e
*
**************************************
*
* Functional description
* Convert from UNIX time structure to internal TIME format.
*
**************************************/
const tm* times = (const struct tm*) times_arg;
*isc_time = ((times->tm_hour * 60 + times->tm_min) * 60 +
times->tm_sec) * ISC_TIME_SECONDS_PRECISION;
}
void API_ROUTINE isc_encode_timestamp(const void* times_arg, GDS_TIMESTAMP* date)
{
/**************************************
*
* i s c _ e n c o d e _ t i m e s t a m p
*
**************************************
*
* Functional description
* Convert from UNIX time structure to internal timestamp format.
*
* Note: the date arguement is really an ISC_TIMESTAMP -- however the
* definition of ISC_TIMESTAMP is not available from all the source
* modules that need to use isc_encode_timestamp
*
**************************************/
const tm* times = (const struct tm*) times_arg;
date->timestamp_date = nday(times);
date->timestamp_time =
((times->tm_hour * 60 + times->tm_min) * 60 +
times->tm_sec) * ISC_TIME_SECONDS_PRECISION;
}
#ifdef DEV_BUILD
void GDS_breakpoint(int parameter)
{
/**************************************
*
* G D S _ b r e a k p o i n t
*
**************************************
*
* Functional description
* Just a handy place to put a debugger breakpoint
* Calls to this can then be embedded for DEV_BUILD
* using the BREAKPOINT(x) macro.
*
**************************************/
/* Put a breakpoint here via the debugger */
}
#endif
SINT64 API_ROUTINE isc_portable_integer(const UCHAR* ptr, SSHORT length)
{
/**************************************
*
* i s c _ p o r t a b l e _ i n t e g e r
*
**************************************
*
* Functional description
* Pick up (and convert) a Little Endian (VAX) style integer
* of length 1, 2, 4 or 8 bytes to local system's Endian format.
*
* various parameter blocks (i.e., dpb, tpb, spb) flatten out multibyte
* values into little endian byte ordering before network transmission.
* programs need to use this function in order to get the correct value
* of the integer in the correct Endian format.
*
* **Note**
*
* This function is similar to gds__vax_integer() in functionality.
* The difference is in the return type. Changed from SLONG to SINT64
* Since gds__vax_integer() is a public API routine, it could not be
* changed. This function has been made public so gbak can use it.
*
**************************************/
SINT64 value;
SSHORT shift;
fb_assert(length <= 8);
value = shift = 0;
while (--length >= 0) {
value += ((SINT64) *ptr++) << shift;
shift += 8;
}
return value;
}
#ifdef DEBUG_GDS_ALLOC
void API_ROUTINE gds_alloc_flag_unfreed(void *blk)
{
/**************************************
*
* g d s _ a l l o c _ f l a g _ u n f r e e d
*
**************************************
*
* Functional description
* Flag a buffer as "known to be unfreed" so we
* don't report it in gds_alloc_report
*
**************************************/
// JMB: need to rework this for the new pools
// Skidder: Not sure we need to rework this routine.
// What we really need is to fix all memory leaks including very old.
}
#endif // DEBUG_GDS_ALLOC
#ifdef DEBUG_GDS_ALLOC
void API_ROUTINE gds_alloc_report(ULONG flags, char* filename, int lineno)
{
/**************************************
*
* g d s _ a l l o c _ r e p o r t
*
**************************************
*
* Functional description
* Print buffers that might be memory leaks.
* Or that might have been clobbered.
*
**************************************/
// Skidder: Calls to this function must be replaced with MemoryPool::print_contents
}
#endif // DEBUG_GDS_ALLOC
/* CVC: See comment below. Basically, it provides the needed const correctness,
but throws away the const to make the callee happy, knowing that the callee
indeed treats vector as it was a pointer with the const prefix.
Maybe this function could be private and thus made inline? */
SLONG API_ROUTINE gds_interprete_cpp(char* const s, const ISC_STATUS** vector)
{
/**************************************
*
* g d s _ i n t e r p r e t e _ c p p
*
**************************************
*
* Functional description
* Translate a status code with arguments to a string. Return the
* length of the string while updating the vector address. If the
* message is null (end of messages) or invalid, return 0;
*
**************************************/
return gds__interprete(s, const_cast<ISC_STATUS**>(vector));
}
/* CVC: This non-const signature is needed for compatibility. The reason is
that, unlike int* that can be assigned to const int* transparently to the caller,
int** CANNOT be assigned to const int**, so applications would have to be
fixed to provide such const int**; while it may be more correct from the
semantic POV, we can't estimate how much work it's for app developers.
Therefore, we go back to the old signature and provide gds_interprete_cpp
for compliance, to be used inside the engine, too. September, 2003. */
SLONG API_ROUTINE gds__interprete(char* s, ISC_STATUS** vector)
{
/**************************************
*
* g d s _ $ i n t e r p r e t e
*
**************************************
*
* Functional description
* Translate a status code with arguments to a string. Return the
* length of the string while updating the vector address. If the
* message is null (end of messages) or invalid, return 0;
*
**************************************/
if (!**vector)
return 0;
const ISC_STATUS* v;
ISC_STATUS code;
/* handle a case: "no errors, some warning(s)" */
if ((*vector)[1] == 0 && (*vector)[2] == isc_arg_warning) {
v = *vector + 4;
code = (*vector)[3];
}
else {
v = *vector + 2;
code = (*vector)[1];
}
TEXT* args[10];
TEXT** arg = args;
/* Parse and collect any arguments that may be present */
TEXT* p;
const TEXT* q;
const SSHORT temp_len = (SSHORT) BUFFER_SMALL;
TEXT* temp = NULL;
SSHORT l;
for (;;) {
const UCHAR x = (UCHAR) *v++;
switch (x) {
case gds_arg_string:
case gds_arg_number:
*arg++ = (TEXT*) *v++;
continue;
case gds_arg_cstring:
if (!temp) {
/* We need a temporary buffer when cstrings are involved.
Give up if we can't get one. */
p = temp = (TEXT*) gds__alloc((SLONG) BUFFER_SMALL);
/* FREE: at procedure exit */
if (!temp) /* NOMEM: */
return 0;
}
l = (SSHORT) *v++;
q = (const TEXT*) *v++;
*arg++ = p;
/* ensure that we do not overflow the buffer allocated */
l = (temp_len < l) ? temp_len : l;
if (l)
do {
*p++ = *q++;
} while (--l);
*p++ = 0;
continue;
default:
break;
}
--v;
break;
}
/* Handle primary code on a system by system basis */
switch ((UCHAR) (*vector)[0]) {
case isc_arg_warning:
case gds_arg_gds:
{
USHORT fac = 0, class_ = 0;
const ISC_STATUS decoded = gds__decode(code, &fac, &class_);
if (gds__msg_format(0, fac, (USHORT) decoded,
128, s, args[0], args[1], args[2], args[3],
args[4]) < 0)
{
if ((decoded < FB_NELEM(messages) - 1) && (decoded >= 0))
sprintf(s, messages[decoded], args[0], args[1], args[2],
args[3], args[4]);
else
sprintf(s, "unknown ISC error %ld", code); /* TXNN */
}
}
break;
case gds_arg_interpreted:
p = s;
q = (const TEXT*) (*vector)[1];
while ((*p++ = *q++) /*!= NULL*/);
break;
case gds_arg_unix:
/* The strerror() function returns the appropriate description
string, or an unknown error message if the error code is unknown. */
p = (TEXT*) strerror(code);
break;
case gds_arg_dos:
sprintf(s, "unknown dos error %ld", code); /* TXNN */
break;
#ifdef VMS
case gds_arg_vms:
{
l = 0;
struct dsc$descriptor_s desc;
desc.dsc$b_class = DSC$K_CLASS_S;
desc.dsc$b_dtype = DSC$K_DTYPE_T;
desc.dsc$w_length = 128;
desc.dsc$a_pointer = s;
TEXT flags[4];
ISC_STATUS status = sys$getmsg(code, &l, &desc, 15, flags);
if (status & 1)
s[l] = 0;
else
sprintf(s, "uninterpreted VMS code %x", code); /* TXNN */
}
break;
#endif
#ifdef WIN_NT
case gds_arg_win32:
if (!(l = (SSHORT) FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
code,
GetUserDefaultLangID(),
s,
128,
NULL))
&& !(l = (SSHORT)FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
code,
0, // TMN: Fallback to system known language
s,
128,
NULL)))
{
sprintf(s, "unknown Win32 error %ld", code); /* TXNN */
}
break;
#endif
default:
if (temp)
gds__free((SLONG *) temp);
return 0;
}
if (temp)
gds__free((SLONG *) temp);
*vector = const_cast<ISC_STATUS*>(v);
const TEXT* end = s;
while (*end)
end++;
return end - s;
}
/* CVC: This special function for ADA has been restored to non-const vector,
too, in case its usage was broken. */
void API_ROUTINE gds__interprete_a(
SCHAR* s,
SSHORT* length,
ISC_STATUS* vector, SSHORT* offset)
{
/**************************************
*
* g d s _ $ i n t e r p r e t e _ a
*
**************************************
*
* Functional description
* Translate a status code with arguments to a string. Return the
* length of the string while updating the vector address. If the
* message is null (end of messages) or invalid, return 0;
* Ada being an unsatisfactory excuse for a language, fall back on
* the concept of indexing into the vector.
*
**************************************/
ISC_STATUS *v = vector + *offset;
*length = (SSHORT) gds__interprete(s, &v);
*offset = v - vector;
}
#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
#ifdef WIN_NT
Firebird::Spinlock trace_mutex;
HANDLE trace_file_handle = INVALID_HANDLE_VALUE;
#endif
void API_ROUTINE gds__trace_raw(const char* text, unsigned int length)
{
/**************************************
*
* g d s _ t r a c e _ r a w
*
**************************************
*
* Functional description
* Write trace event to a log file
* This function tries to be async-signal safe
*
**************************************/
if (!length)
length = strlen(text);
#ifdef WIN_NT
// Note: thread-safe code
// Nickolay Samofatov, 12 Sept 2003. Windows open files extremely slowly.
// Slowly enough to make such trace useless. Thus we cache file handle !
trace_mutex.enter();
while (true) {
if (trace_file_handle == INVALID_HANDLE_VALUE) {
TEXT name[MAXPATHLEN];
gds__prefix(name, LOGFILE);
// We do not care to close this file.
// It will be closed automatically when our process terminates.
trace_file_handle = CreateFile(name, GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (trace_file_handle == INVALID_HANDLE_VALUE)
break;
}
DWORD bytesWritten;
SetFilePointer(trace_file_handle, 0, NULL, FILE_END);
WriteFile(trace_file_handle, text, length, &bytesWritten, NULL);
if (bytesWritten != length) {
// Handle the case when file was deleted by another process on Win9x
// On WinNT we are not going to notice that fact :(
CloseHandle(trace_file_handle);
trace_file_handle = INVALID_HANDLE_VALUE;
continue;
}
break;
}
trace_mutex.leave();
#else
TEXT name[MAXPATHLEN];
// This function is not truly signal safe now.
// It calls string::c_str() and may call getenv(), not good.
// We can only hope that failure is unlikely in it...
gds__prefix(name, LOGFILE);
// Note: signal-safe code
int file = open(name, O_CREAT | O_APPEND | O_WRONLY, 0660);
if (file == -1) return;
write(file, text, length);
close(file);
#endif
}
void API_ROUTINE gds__trace(const TEXT * text)
{
/**************************************
*
* g d s _ t r a c e
*
**************************************
*
* Functional description
* Post trace event to a log file. Function records date and time
* This function tries to be async-signal safe
*
**************************************/
const time_t now = time((time_t *)0); // is specified in POSIX to be signal-safe
// 07 Sept 2003, Nickolay Samofatov.
// Since we cannot call ctime/localtime_r or anything else like this from
// signal hanlders we need to decode time by hand.
struct tm today;
int days, rem;
days = now / SECS_PER_DAY;
rem = now % SECS_PER_DAY;
today.tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
today.tm_min = rem / 60;
today.tm_sec = rem % 60;
ndate(days + 40617 /* Number of first day of the Epoch in GDS counting */,
&today);
char buffer[1024]; // 1K should be enough for the trace message
char *p = buffer;
gds__ulstr(p, today.tm_year+1900, 4, '0'); p+=4;
*p++ = '-';
gds__ulstr(p, today.tm_mon, 2, '0'); p+=2;
*p++ = '-';
gds__ulstr(p, today.tm_mday, 2, '0'); p+=2;
*p++ = 'T';
gds__ulstr(p, today.tm_hour, 2, '0'); p+=2;
*p++ = ':';
gds__ulstr(p, today.tm_min, 2, '0'); p+=2;
*p++ = ':';
gds__ulstr(p, today.tm_sec, 2, '0'); p+=2;
*p++ = ' ';
gds__ulstr(p,
#ifdef WIN_NT
#ifdef SUPERSERVER
GetCurrentThreadId(),
#else
GetCurrentProcessId(),
#endif
#else
getpid(),
#endif
5, ' '); p += 5;
*p++ = ' ';
strcpy(p, text); p += strlen(p);
strcat(p, "\n"); p += strlen(p);
gds__trace_raw(buffer, p - buffer);
}
void API_ROUTINE gds__log(const TEXT* text, ...)
{
/**************************************
*
* g d s _ l o g
*
**************************************
*
* Functional description
* Post an event to a log file.
*
**************************************/
va_list ptr;
IB_FILE *file;
int oldmask;
time_t now;
TEXT name[MAXPATHLEN];
#ifdef HAVE_GETTIMEOFDAY
struct timeval tv;
GETTIMEOFDAY(&tv);
now = tv.tv_sec;
#else
now = time((time_t *)0);
#endif
gds__prefix(name, LOGFILE);
oldmask = umask(0111);
#ifdef WIN_NT
trace_mutex.enter();
#endif
if ((file = ib_fopen(name, FOPEN_APPEND_TYPE)) != NULL)
{
ib_fprintf(file, "\n%s%s\t%.25s\t",
ISC_get_host(name, MAXPATHLEN), gdslogid, ctime(&now));
VA_START(ptr, text);
ib_vfprintf(file, text, ptr);
ib_fprintf(file, "\n\n");
ib_fclose(file);
}
#ifdef WIN_NT
trace_mutex.leave();
#endif
umask(oldmask);
}
void API_ROUTINE gds__log_status(const TEXT* database,
const ISC_STATUS* status_vector)
{
/**************************************
*
* g d s _ $ l o g _ s t a t u s
*
**************************************
*
* Functional description
* Log error to error log.
*
**************************************/
TEXT* const buffer = (TEXT*) gds__alloc((SLONG) BUFFER_XLARGE);
/* FREE: at procedure exit */
if (!buffer) /* NOMEM: */
return;
#ifdef DEV_BUILD
const size_t db_len = strlen(database);
fb_assert(db_len < BUFFER_XLARGE - 2);
fb_assert(db_len < MAXPATHLEN);
#endif
const int interpreted_line_length = 80; // raw estimation
TEXT* p = buffer;
const TEXT* const end = p + BUFFER_XLARGE - interpreted_line_length;
const int max_db_len = int(BUFFER_XLARGE - 12);
sprintf(p, "Database: %.*s", max_db_len, (database) ? database : "");
do {
while (*p)
p++;
*p++ = '\n';
*p++ = '\t';
} while (p < end && gds_interprete_cpp(p, &status_vector));
p[-2] = 0;
gds__log(buffer, 0);
gds__free((SLONG *) buffer);
}
int API_ROUTINE gds__msg_close(void *handle)
{
/**************************************
*
* g d s _ $ m s g _ c l o s e
*
**************************************
*
* Functional description
* Close the message file and zero out static variable.
*
**************************************/
GDS_MSG message = reinterpret_cast < GDS_MSG > (handle);
if (!message) {
if (!default_msg) {
return 0;
}
message = default_msg;
}
default_msg = NULL;
int fd = message->msg_file;
FREE_LIB_MEMORY(message);
if (fd <= 0)
return 0;
return close(fd);
}
SSHORT API_ROUTINE gds__msg_format(void* handle,
USHORT facility,
USHORT number,
USHORT length,
TEXT* buffer,
const TEXT* arg1,
const TEXT* arg2,
const TEXT* arg3,
const TEXT* arg4,
const TEXT* arg5)
{
/**************************************
*
* g d s _ $ m s g _ f o r m a t
*
**************************************
*
* Functional description
* Lookup and format message. Return as much of formatted string
* as fits in caller's buffer.
*
**************************************/
TEXT *p, *formatted, *end;
SLONG size;
int n;
size = (SLONG) (((arg1) ? MAX_ERRSTR_LEN : 0) +
((arg2) ? MAX_ERRSTR_LEN : 0) +
((arg3) ? MAX_ERRSTR_LEN : 0) +
((arg4) ? MAX_ERRSTR_LEN : 0) +
((arg5) ? MAX_ERRSTR_LEN : 0) + MAX_ERRMSG_LEN);
size = (size < length) ? length : size;
formatted = (TEXT *) gds__alloc((SLONG) size);
if (!formatted) /* NOMEM: */
return -1;
/* Let's assume that the text to be output will never be shorter
than the raw text of the message to be formatted. Then we can
use the caller's buffer to temporarily hold the raw text. */
n = gds__msg_lookup(handle, facility, number, length, buffer, NULL);
if (n > 0 && n < length)
{
sprintf(formatted, buffer, arg1, arg2, arg3, arg4, arg5);
}
else
{
sprintf(formatted, "can't format message %d:%d -- ", facility,
number);
if (n == -1)
strcat(formatted, "message text not found");
else if (n == -2) {
strcat(formatted, "message file ");
for (p = formatted; *p;)
p++;
gds__prefix_msg(p, MSG_FILE);
strcat(p, " not found");
}
else {
for (p = formatted; *p;)
p++;
sprintf(p, "message system code %d", n);
}
}
USHORT l = strlen(formatted);
end = buffer + length - 1;
for (p = formatted; *p && buffer < end;) {
*buffer++ = *p++;
}
*buffer = 0;
gds__free((SLONG *) formatted);
return ((n > 0) ? l : -l);
}
SSHORT API_ROUTINE gds__msg_lookup(void* handle,
USHORT facility,
USHORT number,
USHORT length,
TEXT* buffer, USHORT* flags)
{
/**************************************
*
* g d s _ $ m s g _ l o o k u p
*
**************************************
*
* Functional description
* Lookup a message. Return as much of the record as fits in the
* callers buffer. Return the FULL size of the message, or negative
* number if we can't find the message.
*
**************************************/
GDS_MSG message;
int status;
USHORT n;
MSGREC leaf;
MSGNOD node, end;
ULONG position, code;
TEXT *p;
/* Handle default message file */
if (!(message = (GDS_MSG) handle) && !(message = default_msg)) {
/* Try environment variable setting first */
if ((p = getenv("ISC_MSGS")) == NULL ||
(status =
gds__msg_open(reinterpret_cast < void **>(&message), p))) {
TEXT translated_msg_file[sizeof(MSG_FILE_LANG) + LOCALE_MAX + 1];
TEXT *msg_file;
/* Try declared language of this attachment */
/* This is not quite the same as the language declared on the
READY statement */
msg_file = (TEXT *) gds__alloc((SLONG) MAXPATHLEN);
/* FREE: at block exit */
if (!msg_file) /* NOMEM: */
return -2;
p = getenv("LC_MESSAGES");
if (p != NULL) {
sanitize(p);
sprintf(translated_msg_file, MSG_FILE_LANG, p);
gds__prefix_msg(msg_file, translated_msg_file);
status =
gds__msg_open(reinterpret_cast < void **>(&message),
msg_file);
}
else
status = 1;
if (status) {
/* Default to standard message file */
gds__prefix_msg(msg_file, MSG_FILE);
status =
gds__msg_open(reinterpret_cast < void **>(&message),
msg_file);
}
gds__free((SLONG *) msg_file);
}
if (status)
return status;
default_msg = message;
}
/* Search down index levels to the leaf. If we get lost, punt */
code = MSG_NUMBER(facility, number);
end = (MSGNOD) ((SCHAR *) message->msg_bucket + message->msg_bucket_size);
position = message->msg_top_tree;
for (n = 1, status = 0; !status; n++) {
if (lseek(message->msg_file, LSEEK_OFFSET_CAST position, 0) < 0)
status = -6;
else if (read(message->msg_file, message->msg_bucket,
message->msg_bucket_size) < 0)
status = -7;
else if (n == message->msg_levels)
break;
else {
for (node = (MSGNOD) message->msg_bucket; !status; node++) {
if (node >= end) {
status = -8;
break;
}
else if (node->msgnod_code >= code) {
position = node->msgnod_seek;
break;
}
}
}
}
if (!status) {
/* Search the leaf */
for (leaf = (MSGREC) message->msg_bucket; !status;
leaf = NEXT_LEAF(leaf)) {
if (leaf >= (MSGREC) end || leaf->msgrec_code > code) {
status = -1;
break;
}
else if (leaf->msgrec_code == code) {
/* We found the correct message, so return it to the user */
n = MIN(length - 1, leaf->msgrec_length);
memcpy(buffer, leaf->msgrec_text, n);
buffer[n] = 0;
if (flags)
*flags = leaf->msgrec_flags;
status = leaf->msgrec_length;
break;
}
}
}
return status;
}
int API_ROUTINE gds__msg_open(void** handle, const TEXT* filename)
{
/**************************************
*
* g d s _ $ m s g _ o p e n
*
**************************************
*
* Functional description
* Try to open a message file. If successful, return a status of 0
* and update a message handle.
*
**************************************/
int n;
GDS_MSG message;
ISC_MSGHDR header;
if ((n = open(filename, O_RDONLY | O_BINARY, 0)) < 0)
return -2;
if (read(n, &header, sizeof(header)) < 0) {
close(n);
return -3;
}
// Trick to silence compiler when MSG_MINOR_VERSION == 0
USHORT minor = MSG_MINOR_VERSION;
if (header.msghdr_major_version != MSG_MAJOR_VERSION ||
header.msghdr_minor_version < minor) {
close(n);
return -4;
}
message =
(GDS_MSG) ALLOC_LIB_MEMORY((SLONG) sizeof(gds_msg) +
header.msghdr_bucket_size - 1);
/* FREE: in gds__msg_close */
if (!message) { /* NOMEM: return non-open error */
close(n);
return -5;
}
#ifdef DEBUG_GDS_ALLOC
/* This will only be freed if the client closes the message file for us */
gds_alloc_flag_unfreed((void *) message);
#endif
message->msg_file = n;
message->msg_bucket_size = header.msghdr_bucket_size;
message->msg_levels = header.msghdr_levels;
message->msg_top_tree = header.msghdr_top_tree;
*handle = message;
return 0;
}
void API_ROUTINE gds__msg_put(
void* handle,
USHORT facility,
USHORT number,
const TEXT* arg1, const TEXT* arg2,
const TEXT* arg3, const TEXT* arg4,
const TEXT* arg5)
{
/**************************************
*
* g d s _ $ m s g _ p u t
*
**************************************
*
* Functional description
* Lookup and format message. Return as much of formatted string
* as fits in callers buffer.
*
**************************************/
TEXT formatted[512];
gds__msg_format(handle, facility, number, sizeof(TEXT) * BUFFER_MEDIUM,
formatted, arg1, arg2, arg3, arg4, arg5);
gds__put_error(formatted);
}
SLONG API_ROUTINE gds__get_prefix(SSHORT arg_type, const TEXT* passed_string)
{
/**************************************
*
* g d s _ $ g e t _ p r e f i x
*
**************************************
*
* Functional description
* Find appropriate InterBase command line arguments
* for Interbase file prefix.
*
* arg_type is 0 for $INTERBASE, 1 for $INTERBASE_LOCK
* and 2 for $INTERBASE_MSG
*
* Function returns 0 on success and -1 on failure
**************************************/
char *prefix_ptr;
int count = 0;
if (arg_type < IB_PREFIX_TYPE || arg_type > IB_PREFIX_MSG_TYPE)
return ((SLONG) - 1);
switch (arg_type) {
case IB_PREFIX_TYPE:
prefix_ptr = ib_prefix = ib_prefix_val;
break;
case IB_PREFIX_LOCK_TYPE:
prefix_ptr = ib_prefix_lock = ib_prefix_lock_val;
break;
case IB_PREFIX_MSG_TYPE:
prefix_ptr = ib_prefix_msg = ib_prefix_msg_val;
break;
default:
return ((SLONG) - 1);
}
/* the command line argument was 'H' for interbase home */
while (*prefix_ptr++ = *passed_string++) {
/* if the next character is space, newline or carriage return OR
number of characters exceeded */
if (*passed_string == ' ' || *passed_string == 10
|| *passed_string == 13 || (count == MAXPATHLEN)) {
*(prefix_ptr++) = '\0';
break;
}
count++;
}
if (!count) {
prefix_ptr = NULL;
return ((SLONG) - 1);
}
return ((SLONG) 0);
}
#ifndef VMS
void API_ROUTINE gds__prefix(TEXT* resultString, const TEXT* file)
{
/**************************************
*
* g d s _ $ p r e f i x ( n o n - V M S )
*
**************************************
*
* Functional description
* Find appropriate file prefix.
* Override conditional defines with
* the enviroment variable FIREBIRD if it is set.
*
**************************************/
resultString[0] = 0;
if (ib_prefix == NULL) {
if (!(ib_prefix = getenv(FB_ENV)) || ib_prefix[0] == 0) {
{
// Try and get value from config file
const char *regPrefix = Config::getRootDirectory();
size_t len = strlen(regPrefix);
if (len > 0) {
if (len > sizeof(ib_prefix_val)) {
ib_perror("ib_prefix path size too large - truncated");
}
strncpy(ib_prefix_val, regPrefix, sizeof(ib_prefix_val) -1);
ib_prefix_val[sizeof(ib_prefix_val) -1] = 0;
ib_prefix = ib_prefix_val;
}
else {
ib_prefix = FB_PREFIX;
strcat(ib_prefix_val, ib_prefix);
}
}
ib_prefix = ib_prefix_val;
}
}
strcat(resultString, ib_prefix);
safe_concat_path(resultString, file);
}
#endif /* !defined(VMS) */
#ifdef VMS
void API_ROUTINE gds__prefix(TEXT* string, const TEXT* root)
{
/**************************************
*
* g d s _ $ p r e f i x ( V M S )
*
**************************************
*
* Functional description
* Find appropriate InterBase file prefix.
* Override conditional defines with
* the enviroment variable INTERBASE if it is set.
*
**************************************/
TEXT temp[256], *p, *q;
ISC_VMS_PREFIX prefix;
SSHORT len;
if (*root != '[') {
strcpy(string, root);
return;
}
/* Check for the existence of an InterBase logical name. If there is
one use it, otherwise use the system directories. */
if (ISC_expand_logical_once(ISC_LOGICAL, sizeof(ISC_LOGICAL) - 2, temp)) {
strcpy(string, ISC_LOGICAL);
strcat(string, root);
return;
}
for (p = temp, q = root; *p = UPPER7(*q); q++)
if (*p++ == ']')
break;
len = p - temp;
for (prefix = trans_prefix; prefix->isc_prefix; prefix++)
if (!strncmp(temp, prefix->isc_prefix, len)) {
strcpy(string, prefix->vms_prefix);
strcat(string, &root[len]);
return;
}
strcpy(string, &root[len]);
}
#endif
#ifndef VMS
void API_ROUTINE gds__prefix_lock(TEXT* string, const TEXT* root)
{
/********************************************************
*
* g d s _ $ p r e f i x _ l o c k ( n o n - V M S )
*
********************************************************
*
* Functional description
* Find appropriate Firebird lock file prefix.
* Override conditional defines with the enviroment
* variable FIREBIRD_LOCK if it is set.
*
**************************************/
string[0] = 0;
if (ib_prefix_lock == NULL) {
if (!(ib_prefix_lock = getenv(FB_LOCK_ENV))) {
#ifdef EMBEDDED
ib_prefix_lock = ib_prefix_lock_val;
gds__temp_dir(ib_prefix_lock);
// Generate filename based on the current PID
TEXT tmp_buf[MAXPATHLEN];
sprintf(tmp_buf, FB_PID_FILE, getpid());
sprintf(tmp_buf, root, tmp_buf);
root = tmp_buf;
#else
ib_prefix_lock = ib_prefix_lock_val;
gds__prefix(ib_prefix_lock, "");
#endif
}
else {
strcat(ib_prefix_lock_val, ib_prefix_lock);
ib_prefix_lock = ib_prefix_lock_val;
}
}
strcat(string, ib_prefix_lock);
safe_concat_path(string, root);
}
#endif
#ifdef VMS
void API_ROUTINE gds__prefix_lock(TEXT* string, const TEXT* root)
{
/************************************************
*
* g d s _ $ p r e f i x _ l o c k ( V M S )
*
************************************************
*
* Functional description
* Find appropriate Firebird lock file prefix.
* Override conditional defines with the enviroment
* variable FIREBIRD_LOCK if it is set.
*
*************************************************/
TEXT temp[256], *p, *q;
ISC_VMS_PREFIX prefix;
SSHORT len;
if (*root != '[') {
strcpy(string, root);
return;
}
/* Check for the existence of a Firebird logical name. If there is
one use it, otherwise use the system directories. */
if (ISC_expand_logical_once
(ISC_LOGICAL_LOCK, sizeof(ISC_LOGICAL_LOCK) - 2, temp)) {
strcpy(string, ISC_LOGICAL_LOCK);
strcat(string, root);
return;
}
for (p = temp, q = root; *p = UPPER7(*q); q++)
if (*p++ == ']')
break;
len = p - temp;
for (prefix = trans_prefix; prefix->isc_prefix; prefix++)
if (!strncmp(temp, prefix->isc_prefix, len)) {
strcpy(string, prefix->vms_prefix);
strcat(string, &root[len]);
return;
}
strcpy(string, &root[len]);
}
#endif
#ifndef VMS
void API_ROUTINE gds__prefix_msg(TEXT* string, const TEXT* root)
{
/********************************************************
*
* g d s _ $ p r e f i x _ m s g ( n o n - V M S )
*
********************************************************
*
* Functional description
* Find appropriate Firebird message file prefix.
* Override conditional defines with the enviroment
* variable FIREBIRD_MSG if it is set.
*
*
**************************************/
string[0] = 0;
if (ib_prefix_msg == NULL) {
if (!(ib_prefix_msg = getenv(FB_MSG_ENV))) {
ib_prefix_msg = ib_prefix_msg_val;
gds__prefix(ib_prefix_msg, "");
}
else {
strcat(ib_prefix_msg_val, ib_prefix_msg);
ib_prefix_msg = ib_prefix_msg_val;
}
}
strcat(string, ib_prefix_msg);
safe_concat_path(string, root);
}
#endif
#ifdef VMS
void API_ROUTINE gds__prefix_msg(TEXT* string, const TEXT* root)
{
/************************************************
*
* g d s _ $ p r e f i x _ m s g ( V M S )
*
************************************************
*
* Functional description
* Find appropriate Firebird message file prefix.
* Override conditional defines with the enviroment
* variable FIREBIRD_MSG if it is set.
*
*************************************************/
TEXT temp[256], *p, *q;
ISC_VMS_PREFIX prefix;
SSHORT len;
if (*root != '[') {
strcpy(string, root);
return;
}
/* Check for the existence of an InterBase logical name. If there is
one use it, otherwise use the system directories. */
/* ISC_LOGICAL_MSG macro needs to be defined, check non VMS version of routine
for functionality. */
if (ISC_expand_logical_once
(ISC_LOGICAL_MSG, sizeof(ISC_LOGICAL_MSG) - 2, te mp)) {
strcpy(string, ISC_LOGICAL_MSG);
strcat(string, root);
return;
}
for (p = temp, q = root; *p = UPPER7(*q); q++)
if (*p++ == ']')
break;
len = p - temp;
for (prefix = trans_prefix; prefix->isc_prefix; prefix++)
if (!strncmp(temp, prefix->isc_prefix, len)) {
strcpy(string, prefix->vms_prefix);
strcat(string, &root[len]);
return;
}
strcpy(string, &root[len]);
}
#endif
ISC_STATUS API_ROUTINE gds__print_status(const ISC_STATUS* vec)
{
/**************************************
*
* g d s _ p r i n t _ s t a t u s
*
**************************************
*
* Functional description
* Interprete a status vector.
*
**************************************/
if (!vec || (!vec[1] && vec[2] == gds_arg_end))
return FB_SUCCESS;
TEXT* s = (TEXT *) gds__alloc((SLONG) BUFFER_LARGE);
/* FREE: at procedure return */
if (!s) /* NOMEM: */
return vec[1];
const ISC_STATUS* vector = vec;
if (!gds_interprete_cpp(s, &vector)) {
gds__free((SLONG *) s);
return vec[1];
}
gds__put_error(s);
s[0] = '-';
while (gds_interprete_cpp(s + 1, &vector))
gds__put_error(s);
gds__free((SLONG *) s);
return vec[1];
}
USHORT API_ROUTINE gds__parse_bpb(USHORT bpb_length,
const UCHAR* bpb,
USHORT* source,
USHORT* target)
{
/**************************************
*
* g d s _ p a r s e _ b p b
*
**************************************
*
* Functional description
* Get blob type, source sub_type and target sub-type
* from a blob parameter block.
*
**************************************/
/* SIGN ERROR */
return gds__parse_bpb2(bpb_length, bpb, (SSHORT*)source, (SSHORT*)target, NULL, NULL);
}
USHORT API_ROUTINE gds__parse_bpb2(USHORT bpb_length,
const UCHAR* bpb,
SSHORT* source,
SSHORT* target,
USHORT* source_interp,
USHORT* target_interp)
{
/**************************************
*
* g d s _ p a r s e _ b p b 2
*
**************************************
*
* Functional description
* Get blob type, source sub_type and target sub-type
* from a blob parameter block.
* Basically gds__parse_bpb() with additional:
* source_interp and target_interp.
*
**************************************/
USHORT type, length;
type = *source = *target = 0;
if (source_interp)
*source_interp = 0;
if (target_interp)
*target_interp = 0;
if (!bpb_length || !bpb)
return type;
const UCHAR* p = bpb;
const UCHAR* const end = p + bpb_length;
if (*p++ != gds_bpb_version1)
return type;
while (p < end) {
const UCHAR op = *p++;
length = *p++;
switch (op) {
case gds_bpb_source_type:
*source = (USHORT) gds__vax_integer(p, length);
break;
case gds_bpb_target_type:
*target = (USHORT) gds__vax_integer(p, length);
break;
case gds_bpb_type:
type = (USHORT) gds__vax_integer(p, length);
break;
case gds_bpb_source_interp:
if (source_interp)
*source_interp = (USHORT) gds__vax_integer(p, length);
break;
case gds_bpb_target_interp:
if (target_interp)
*target_interp = (USHORT) gds__vax_integer(p, length);
break;
default:
break;
}
p += length;
}
return type;
}
SLONG API_ROUTINE gds__ftof(const SCHAR* string,
const USHORT length1,
SCHAR* field,
const USHORT length2)
{
/**************************************
*
* g d s _ f t o f
*
**************************************
*
* Functional description
* Move a fixed length string to a fixed length string.
* This is typically generated by the preprocessor to
* move strings around.
*
**************************************/
USHORT fill = 0;
// CVC: a logic bug rendered the fill > 0 test useless
if (length2 > length1)
fill = length2 - length1;
USHORT l = MIN(length1, length2);
if (l > 0)
do {
*field++ = *string++;
} while (--l);
if (fill > 0)
do {
*field++ = ' ';
} while (--fill);
return 0;
}
int API_ROUTINE gds__print_blr(
const UCHAR* blr,
FPTR_PRINT_CALLBACK routine,
void* user_arg, SSHORT language)
{
/**************************************
*
* g d s _ $ p r i n t _ b l r
*
**************************************
*
* Functional description
* Pretty print blr thru callback routine.
*
**************************************/
try {
gds_ctl ctl;
gds_ctl* control = &ctl;
SSHORT level = 0;
SLONG offset = 0;
if (!routine) {
routine = gds__default_printer;
user_arg = NULL;
}
control->ctl_routine = routine;
control->ctl_user_arg = user_arg;
control->ctl_blr = control->ctl_blr_start = blr;
control->ctl_ptr = control->ctl_buffer;
control->ctl_language = language;
const SSHORT version = BLR_BYTE;
if ((version != blr_version4) && (version != blr_version5))
blr_error(control, "*** blr version %d is not supported ***",
(int) version);
blr_format(control,
(version == blr_version4) ? "blr_version4," : "blr_version5,");
PRINT_LINE;
PRINT_VERB;
offset = control->ctl_blr - control->ctl_blr_start;
const SCHAR eoc = BLR_BYTE;
if (eoc != blr_eoc)
blr_error(control, "*** expected end of command, encounted %d ***",
(int) eoc);
blr_format(control, "blr_eoc");
PRINT_LINE;
} // try
catch (const std::exception&) {
return -1;
}
return 0;
}
void API_ROUTINE gds__put_error(const TEXT* string)
{
/**************************************
*
* g d s _ p u t _ e r r o r
*
**************************************
*
* Functional description
* Put out a line of error text.
*
**************************************/
#ifdef VMS
#define PUT_ERROR
struct dsc$descriptor_s desc;
ISC_make_desc(string, &desc, 0);
lib$put_output(&desc);
#endif
#ifdef PUT_ERROR
#undef PUT_ERROR
#else
ib_fputs(string, ib_stderr);
ib_fputc('\n', ib_stderr);
ib_fflush(ib_stderr);
#endif
}
void API_ROUTINE gds__qtoq(const void* quad_in, void* quad_out)
{
/**************************************
*
* g d s _ q t o q
*
**************************************
*
* Functional description
* Move a quad value to another quad value. This
* call is generated by the preprocessor when assigning
* quad values in FORTRAN.
*
**************************************/
*((GDS_QUAD *) quad_out) = *((GDS_QUAD *) quad_in);
}
void API_ROUTINE gds__register_cleanup(FPTR_VOID_PTR routine, void* arg)
{
/**************************************
*
* g d s _ r e g i s t e r _ c l e a n u p
*
**************************************
*
* Functional description
* Register a cleanup handler.
*
**************************************/
/*
* Ifdef out for windows client. We have not implemented any way of
* determining when a task ends, therefore this never gets called.
*/
CLEAN clean;
if (!initialized)
init();
clean = (CLEAN) ALLOC_LIB_MEMORY((SLONG) sizeof(struct clean));
clean->clean_next = cleanup_handlers;
cleanup_handlers = clean;
clean->clean_routine = routine;
clean->clean_arg = arg;
#ifdef DEBUG_GDS_ALLOC
/* Startup function - known to be unfreed */
gds_alloc_flag_unfreed((void *) clean);
#endif
}
SLONG API_ROUTINE gds__sqlcode(const ISC_STATUS* status_vector)
{
/**************************************
*
* g d s _ s q l c o d e
*
**************************************
*
* Functional description
* Translate GDS error code to SQL error code. This is a little
* imprecise (to say the least) because we don't know the proper
* SQL codes. One must do what what can; stiff upper lip, and all
* that.
*
* Scan a status vector - if we find gds__sqlerr in the "right"
* positions, return the code that follows. Otherwise, for the
* first code for which there is a non-generic SQLCODE, return it.
*
**************************************/
if (!status_vector) {
DEV_REPORT("gds__sqlcode: NULL status vector");
return GENERIC_SQLCODE;
}
bool have_sqlcode = false;
SLONG sqlcode = GENERIC_SQLCODE; /* error of last resort */
/* SQL code -999 (GENERIC_SQLCODE) is generic, meaning "no other sql code
* known". Now scan the status vector, seeing if there is ANY sqlcode
* reported. Make note of the first error in the status vector who's
* SQLCODE is NOT -999, that will be the return code if there is no specific
* sqlerr reported.
*/
const ISC_STATUS* s = status_vector;
while (*s != gds_arg_end)
{
if (*s == gds_arg_gds)
{
s++;
if (*s == gds_sqlerr)
{
return *(s + 2);
}
if (!have_sqlcode) {
/* Now check the hard-coded mapping table of gds_codes to
sql_codes */
USHORT fac = 0, class_ = 0;
USHORT code = (USHORT) gds__decode(status_vector[1], &fac, &class_);
if ((code < FB_NELEM(gds__sql_code)) &&
(gds__sql_code[code] != GENERIC_SQLCODE))
{
sqlcode = gds__sql_code[code];
have_sqlcode = true;
}
}
s++;
}
else if (*s == gds_arg_cstring)
s += 3; /* skip: gds_arg_cstring <len> <ptr> */
else
s += 2; /* skip: gds_arg_* <item> */
};
return sqlcode;
}
void API_ROUTINE gds__sqlcode_s(const ISC_STATUS* status_vector, ULONG* sqlcode)
{
/**************************************
*
* g d s _ s q l c o d e _ s
*
**************************************
*
* Functional description
* Translate GDS error code to SQL error code. This is a little
* imprecise (to say the least) because we don't know the proper
* SQL codes. One must do what what can; stiff upper lip, and all
* that. THIS IS THE COBOL VERSION. (Some cobols don't have
* return values for calls...
*
**************************************/
*sqlcode = gds__sqlcode(status_vector);
}
void API_ROUTINE gds__temp_dir(TEXT * buffer)
{
/**************************************
*
* g d s _ _ t e m p _ d i r
*
**************************************
*
* Functional description
* Return temporary directory.
*
**************************************/
TEXT * directory = 0;
if (!(directory = getenv(FB_TMP_ENV))) {
#ifdef WIN_NT
TEXT temp_dir[MAXPATHLEN];
DWORD len;
// This checks "TEMP" and "TMP" environment variables
if ((len = GetTempPath(sizeof(temp_dir), temp_dir)) &&
len < sizeof(temp_dir))
directory = temp_dir;
#else
directory = getenv("TMP");
#endif
}
if (!directory || strlen(directory) >= MAXPATHLEN)
directory = WORKFILE;
strcpy(buffer, directory);
}
void* API_ROUTINE gds__temp_file(
BOOLEAN stdio_flag, const TEXT* string,
TEXT* expanded_string, TEXT* dir, BOOLEAN unlink_flag)
{
/**************************************
*
* g d s _ _ t e m p _ f i l e
*
**************************************
*
* Functional description
* Create and open a temp file with a given location.
* Unless the address of a buffer for the expanded file name string is
* given, make up the file "pre-deleted". Return -1 on failure.
* If unlink_flag is TRUE than file is marked as pre-deleted even if
* expanded_string is not NULL.
* NOTE
* Function returns untyped handle that needs to be casted to either IB_FILE
* or used as file descriptor. This is ugly and needs to be fixed probably
* via introducing two functions with different return types.
*
**************************************/
TEXT temp_dir[MAXPATHLEN];
TEXT *directory = dir;
if (!directory) {
gds__temp_dir(temp_dir);
directory = temp_dir;
}
if (strlen(directory) >= MAXPATHLEN-strlen(string)-strlen(TEMP_PATTERN)-2)
return (void *)-1;
void *result;
#ifdef WIN_NT
/* These are the characters used in temporary filenames. */
static const char letters[] = "abcdefghijklmnopqrstuvwxyz0123456789";
_timeb t;
_ftime(&t);
__int64 randomness = t.time;
randomness *= 1000;
randomness += t.millitm;
for (int tryCount = 0; tryCount < MAX_TMPFILE_TRIES; tryCount++) {
char file_name[MAXPATHLEN];
strcpy(file_name, directory);
if (file_name[strlen(file_name)-1] != '\\')
strcat(file_name, "\\");
strcat(file_name, string);
char suffix[] = TEMP_PATTERN;
__int64 temp = randomness;
for (size_t i=0; i< sizeof(suffix)-1; i++) {
suffix[i] = letters[temp % (sizeof(letters)-1)];
temp /= sizeof(letters)-1;
}
strcat(file_name, suffix);
if (expanded_string)
strcpy(expanded_string, file_name);
result = (void*)_sopen(file_name, _O_CREAT | _O_TRUNC | _O_RDWR |
_O_BINARY | _O_SHORT_LIVED | _O_NOINHERIT | _O_EXCL |
(expanded_string && !unlink_flag ? 0 : _O_TEMPORARY),
_SH_DENYRW, _S_IREAD|_S_IWRITE);
if ((int)result != -1 || (errno != EACCES && errno != EEXIST))
break;
randomness++;
}
if ((int)result == -1) return result;
if (stdio_flag) {
if (!(result = ib_fdopen((int) result, "w+b")))
return (void *)-1;
}
#else
TEXT file_name[MAXPATHLEN];
strcpy(file_name, directory);
if (file_name[strlen(file_name)-1] != '/')
strcat(file_name, "/");
strcat(file_name, string);
strcat(file_name, TEMP_PATTERN);
#ifdef HAVE_MKSTEMP
result = (void *)mkstemp(file_name);
#else
if (mktemp(file_name) == (char *)0)
return (void *)-1;
do {
result = (void *)open(file_name, O_RDWR | O_EXCL| O_CREAT);
} while (result == (void *)-1 && errno == EINTR);
#endif
if (result == (void *)-1)
return result;
if (stdio_flag)
if (!(result = ib_fdopen((int) result, "w+")))
return (void *)-1;
if (expanded_string)
strcpy(expanded_string, file_name);
if (!expanded_string || unlink_flag)
unlink(file_name);
#endif
return result;
}
void API_ROUTINE gds__unregister_cleanup(FPTR_VOID_PTR routine, void *arg)
{
/**************************************
*
* g d s _ u n r e g i s t e r _ c l e a n u p
*
**************************************
*
* Functional description
* Unregister a cleanup handler.
*
**************************************/
CLEAN *clean_ptr, clean;
for (clean_ptr = &cleanup_handlers; clean = *clean_ptr;
clean_ptr = &clean->clean_next) {
if (clean->clean_routine == routine
&& clean->clean_arg == arg) {
*clean_ptr = clean->clean_next;
FREE_LIB_MEMORY(clean);
break;
}
}
}
#ifndef VMS
BOOLEAN API_ROUTINE gds__validate_lib_path(const TEXT* module,
const TEXT* ib_env_var,
TEXT* resolved_module,
SLONG length)
{
/**************************************
*
* g d s _ $ v a l i d a t e _ l i b _ p a t h ( n o n - V M S )
*
**************************************
*
* Functional description
* Find the external library path variable.
* Validate that the path to the library module name
* in the path specified. If the external lib path
* is not defined then accept any path, and return
* TRUE. If the module is in the path then return TRUE
* else, if the module is not in the path return FALSE.
*
**************************************/
TEXT *p, *q;
TEXT *token;
TEXT abs_module[MAXPATHLEN];
TEXT abs_module_path[MAXPATHLEN];
TEXT abs_path[MAXPATHLEN];
TEXT path[MAXPATHLEN];
TEXT *ib_ext_lib_path = 0;
if (!(ib_ext_lib_path = getenv(ib_env_var))) {
strncpy(resolved_module, module, length);
return TRUE; /* The variable is not defined. Retrun TRUE */
}
if (EXPAND_PATH(module, abs_module)) {
/* Extract the path from the absolute module name */
for (p = abs_module, q = NULL; *p; p++)
if ((*p == '\\') || (*p == '/'))
q = p;
memset(abs_module_path, 0, MAXPATHLEN);
strncpy(abs_module_path, abs_module, q - abs_module);
/* Check to see if the module path is in the lib path
if it is return TURE. If it does not find it, then
the module path is not valid so return FALSE */
token = strtok(ib_ext_lib_path, ";");
while (token != NULL) {
strcpy(path, token);
/* make sure that there is no traing slash on the path */
p = path + strlen(path);
if ((p != path) && ((p[-1] == '/') || (p[-1] == '\\')))
p[-1] = 0;
if ((EXPAND_PATH(path, abs_path))
&& (!COMPARE_PATH(abs_path, abs_module_path))) {
strncpy(resolved_module, abs_module, length);
return TRUE;
}
token = strtok(NULL, ";");
}
}
return FALSE;
}
#endif
#ifdef VMS
BOOLEAN API_ROUTINE gds__validate_lib_path(const TEXT* module,
const TEXT* ib_env_var,
TEXT* resolved_module,
SLONG length)
{
/**************************************
*
* g d s _ $ v a l i d a t e _ l i b _ p a t h ( V M S )
*
**************************************
*
* Functional description
* Find the InterBase external library path variable.
* Validate that the path to the library module name
* in the path specified. If the external lib path
* is not defined then accept any path, and return true.
* If the module is not in the path return FALSE.
*
**************************************/
TEXT *p, *q;
TEXT path[MAXPATHLEN];
TEXT abs_module_path[MAXPATHLEN];
TEXT abs_module[MAXPATHLEN];
/* Check for the existence of an InterBase logical name. If there is
one use it, otherwise use the system directories. */
COMPILER ERROR ! BEFORE DOING A VMS POST PLEASE
MAKE SURE THAT THIS FUNCTION WORKS THE SAME WAY
AS THE NON -
VMS ONE
DOES.if (!
(ISC_expand_logical_once
(ib_env_var, strlen(ib_env_var) - 2, path))) return TRUE;
if (ISC_expand_logical_once(module, strlen(module) - 2, abs_module)) {
/* Extract the path from the module name */
for (p = abs_module, q = NULL; *p; p++)
if (*p == ']')
q = p;
memset(abs_module_path, 0, MAXPATHLEN);
strncpy(abs_module_path, abs_module, q - abs_module + 1);
/* Check to see if the module path is the same as lib path
if it is return TURE. If not then the module path is
not valid so return FALSE */
if (!strcmp(path, abs_module_path))
return TRUE;
}
return FALSE;
}
#endif
SLONG API_ROUTINE gds__vax_integer(const UCHAR* ptr, SSHORT length)
{
/**************************************
*
* g d s _ $ v a x _ i n t e g e r
*
**************************************
*
* Functional description
* Pick up (and convert) a VAX style integer of length 1, 2, or 4
* bytes.
*
**************************************/
SLONG value;
SSHORT shift;
value = shift = 0;
while (--length >= 0) {
value += ((SLONG) * ptr++) << shift;
shift += 8;
}
return value;
}
void API_ROUTINE gds__vtof(
const SCHAR* string,
SCHAR* field, USHORT length)
{
/**************************************
*
* g d s _ v t o f
*
**************************************
*
* Functional description
* Move a null terminated string to a fixed length
* field. The call is primarily generated by the
* preprocessor.
*
**************************************/
while (*string) {
*field++ = *string++;
if (--length <= 0)
return;
}
if (length > 0)
do
*field++ = ' ';
while (--length);
}
void API_ROUTINE gds__vtov(const SCHAR* string, char* field, SSHORT length)
{
/**************************************
*
* g d s _ v t o v
*
**************************************
*
* Functional description
* Move a null terminated string to a fixed length
* field. The call is primarily generated by the
* preprocessor. Unless gds__vtof, the target string
* is null terminated.
*
**************************************/
--length;
while ((*field++ = *string++) != 0)
if (--length <= 0) {
*field = 0;
return;
}
}
void API_ROUTINE isc_print_sqlerror(SSHORT sqlcode, const ISC_STATUS* status)
{
/**************************************
*
* i s c _ p r i n t _ s q l e r r o r
*
**************************************
*
* Functional description
* Given an sqlcode, give as much info as possible.
* Decide whether status is worth mentioning.
*
**************************************/
TEXT error_buffer[192], *p;
sprintf(error_buffer, "SQLCODE: %d\nSQL ERROR:\n", sqlcode);
for (p = error_buffer; *p;)
p++;
isc_sql_interprete(sqlcode, p,
(SSHORT) (sizeof(error_buffer) - (p - error_buffer) -
2));
while (*p)
p++;
*p++ = '\n';
*p = 0;
gds__put_error(error_buffer);
if (status && status[1]) {
gds__put_error("ISC STATUS: ");
gds__print_status(status);
}
}
void API_ROUTINE isc_sql_interprete(
SSHORT sqlcode,
TEXT* buffer, SSHORT length)
{
/**************************************
*
* i s c _ s q l _ i n t e r p r e t e
*
**************************************
*
* Functional description
* Given an sqlcode, return a buffer full of message
* This is very simple because all we have is the
* message number, and the sqlmessages have as yet no
* arguments
*
* NOTE: As of 21-APR-1999, sqlmessages HAVE arguments hence use
* an empty string instead of NULLs.
*
**************************************/
TEXT *str = "";
if (sqlcode < 0)
gds__msg_format(0, 13, (USHORT) (1000 + sqlcode), length, buffer,
str, str, str, str, str);
else
gds__msg_format(0, 14, sqlcode, length, buffer,
str, str, str, str, str);
}
#ifdef VMS
int unlink(SCHAR * file)
{
/**************************************
*
* u n l i n k
*
**************************************
*
* Functional description
* Get rid of a file and all of its versions.
*
**************************************/
int status;
struct dsc$descriptor_s desc;
ISC_make_desc(file, &desc, 0);
for (;;) {
status = lib$delete_file(&desc);
if (!(status & 1))
break;
}
if (!status)
return 0;
else if (status != RMS$_FNF)
return -1;
else
return 0;
}
#endif
static void blr_error(gds_ctl* control, const TEXT* string, ...)
{
/**************************************
*
* b l r _ e r r o r
*
**************************************
*
* Functional description
* Report back an error message and unwind.
*
**************************************/
USHORT offset;
va_list args;
VA_START(args, string);
blr_format(control, string, args);
offset = 0;
PRINT_LINE;
Firebird::status_exception::raise(-1);
}
static void blr_format(gds_ctl* control, const char* string, ...)
{
/**************************************
*
* b l r _ f o r m a t
*
**************************************
*
* Functional description
* Format an utterance.
*
**************************************/
va_list ptr;
VA_START(ptr, string);
vsprintf(control->ctl_ptr, string, ptr);
while (*control->ctl_ptr)
control->ctl_ptr++;
}
static void blr_indent(gds_ctl* control, SSHORT level)
{
/**************************************
*
* b l r _ i n d e n t
*
**************************************
*
* Functional description
* Indent for pretty printing.
*
**************************************/
level *= 3;
while (--level >= 0)
PUT_BYTE(' ');
}
static void blr_print_blr(gds_ctl* control, UCHAR operator_)
{
/**************************************
*
* b l r _ p r i n t _ b l r
*
**************************************
*
* Functional description
* Print a blr item.
*
**************************************/
SCHAR *p;
if (operator_ > FB_NELEM(blr_table) ||
!(p = (SCHAR *) /* const_cast */ blr_table[operator_].blr_string))
blr_error(control, "*** blr operator %d is undefined ***",
(int) operator_);
blr_format(control, "blr_%s, ", p);
}
static SCHAR blr_print_byte(gds_ctl* control)
{
/**************************************
*
* b l r _ p r i n t _ b y t e
*
**************************************
*
* Functional description
* Print a byte as a numeric value and return same.
*
**************************************/
UCHAR v;
v = BLR_BYTE;
blr_format(control, (control->ctl_language) ? "chr(%d), " : "%d, ",
(int) v);
return v;
}
static SCHAR blr_print_char(gds_ctl* control)
{
/**************************************
*
* b l r _ p r i n t _ c h a r
*
**************************************
*
* Functional description
* Print a byte as a numeric value and return same.
*
**************************************/
SCHAR c;
UCHAR v;
SSHORT printable;
v = c = BLR_BYTE;
printable = (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '$' ||
c == '_';
if (printable)
blr_format(control, "'%c',", (char) c);
else if (control->ctl_language)
blr_format(control, "chr(%d),", (int) v);
else
blr_format(control, "%d,", (int) c);
return c;
}
static void blr_print_cond(gds_ctl* control)
{
/**************************************
*
* b l r _ p r i n t _ c o n d
*
**************************************
*
* Functional description
* Print an error condition.
*
**************************************/
USHORT ctype;
SSHORT n;
ctype = BLR_BYTE;
switch (ctype) {
case blr_gds_code:
blr_format(control, "blr_gds_code, ");
n = PRINT_BYTE;
while (--n >= 0)
PRINT_CHAR;
break;
case blr_exception:
blr_format(control, "blr_exception, ");
n = PRINT_BYTE;
while (--n >= 0)
PRINT_CHAR;
break;
case blr_sql_code:
blr_format(control, "blr_sql_code, ");
PRINT_WORD;
break;
case blr_default_code:
blr_format(control, "blr_default_code, ");
break;
default:
blr_error(control, "*** invalid condition type ***");
break;
}
return;
}
static int blr_print_dtype(gds_ctl* control)
{
/**************************************
*
* b l r _ p r i n t _ d t y p e
*
**************************************
*
* Functional description
* Print a datatype sequence and return the length of the
* data described.
*
**************************************/
USHORT dtype;
SCHAR *string;
SSHORT length;
UCHAR v1, v2;
dtype = BLR_BYTE;
/* Special case blob (261) to keep down the size of the
jump table */
switch (dtype) {
case blr_short:
string = "short";
length = 2;
break;
case blr_long:
string = "long";
length = 4;
break;
case blr_int64:
string = "int64";
length = 8;
break;
case blr_quad:
string = "quad";
length = 8;
break;
case blr_timestamp:
string = "timestamp";
length = 8;
break;
case blr_sql_time:
string = "sql_time";
length = 4;
break;
case blr_sql_date:
string = "sql_date";
length = 4;
break;
case blr_float:
string = "float";
length = 4;
break;
case blr_double:
string = "double";
/* for double literal, return the length of the numeric string */
v1 = *(control->ctl_blr);
v2 = *(control->ctl_blr + 1);
length = ((v2 << 8) | v1) + 2;
break;
case blr_d_float:
string = "d_float";
length = 8;
break;
case blr_text:
string = "text";
break;
case blr_cstring:
string = "cstring";
break;
case blr_varying:
string = "varying";
break;
case blr_text2:
string = "text2";
break;
case blr_cstring2:
string = "cstring2";
break;
case blr_varying2:
string = "varying2";
break;
default:
blr_error(control, "*** invalid data type ***");
break;
}
blr_format(control, "blr_%s, ", string);
switch (dtype) {
case blr_text:
length = PRINT_WORD;
break;
case blr_varying:
length = PRINT_WORD + 2;
break;
case blr_short:
case blr_long:
case blr_quad:
case blr_int64:
PRINT_BYTE;
break;
case blr_text2:
PRINT_WORD;
length = PRINT_WORD;
break;
case blr_varying2:
PRINT_WORD;
length = PRINT_WORD + 2;
break;
case blr_cstring2:
PRINT_WORD;
length = PRINT_WORD;
break;
default:
if (dtype == blr_cstring)
length = PRINT_WORD;
break;
}
return length;
}
static void blr_print_join(gds_ctl* control)
{
/**************************************
*
* b l r _ p r i n t _ j o i n
*
**************************************
*
* Functional description
* Print a join type.
*
**************************************/
USHORT join_type;
SCHAR *string;
join_type = BLR_BYTE;
switch (join_type) {
case blr_inner:
string = "inner";
break;
case blr_left:
string = "left";
break;
case blr_right:
string = "right";
break;
case blr_full:
string = "full";
break;
default:
blr_error(control, "*** invalid join type ***");
break;
}
blr_format(control, "blr_%s, ", string);
}
static SLONG blr_print_line(gds_ctl* control, SSHORT offset)
{
/**************************************
*
* b l r _ p r i n t _ l i n e
*
**************************************
*
* Functional description
* Invoke callback routine to print (or do something with) a line.
*
**************************************/
*control->ctl_ptr = 0;
(*control->ctl_routine)(control->ctl_user_arg, offset, control->ctl_buffer);
control->ctl_ptr = control->ctl_buffer;
return control->ctl_blr - control->ctl_blr_start;
}
static void blr_print_verb(gds_ctl* control, SSHORT level)
{
/**************************************
*
* b l r _ p r i n t _ v e r b
*
**************************************
*
* Functional description
* Primary recursive routine to print BLR.
*
**************************************/
SSHORT n, n2;
SLONG offset;
offset = control->ctl_blr - control->ctl_blr_start;
blr_indent(control, level);
UCHAR blr_operator = BLR_BYTE;
if ((SCHAR) blr_operator == (SCHAR) blr_end) {
blr_format(control, "blr_end, ");
PRINT_LINE;
return;
}
blr_print_blr(control, blr_operator);
level++;
const UCHAR *ops = blr_table[blr_operator].blr_operators;
while (*ops)
switch (*ops++) {
case op_verb:
PRINT_VERB;
break;
case op_line:
offset = PRINT_LINE;
break;
case op_byte:
n = PRINT_BYTE;
break;
case op_word:
n = PRINT_WORD;
break;
case op_pad:
PUT_BYTE(' ');
break;
case op_dtype:
n = PRINT_DTYPE;
break;
case op_literal:
while (--n >= 0)
PRINT_CHAR;
break;
case op_join:
PRINT_JOIN;
break;
case op_message:
while (--n >= 0) {
blr_indent(control, level);
PRINT_DTYPE;
offset = PRINT_LINE;
}
break;
case op_parameters:
level++;
while (--n >= 0)
PRINT_VERB;
level--;
break;
case op_error_handler:
while (--n >= 0) {
blr_indent(control, level);
PRINT_COND;
offset = PRINT_LINE;
}
break;
case op_set_error:
PRINT_COND;
break;
case op_indent:
blr_indent(control, level);
break;
case op_begin:
while ((SCHAR) * (control->ctl_blr) != (SCHAR) blr_end)
PRINT_VERB;
break;
case op_map:
while (--n >= 0) {
blr_indent(control, level);
PRINT_WORD;
offset = PRINT_LINE;
PRINT_VERB;
}
break;
case op_args:
while (--n >= 0)
PRINT_VERB;
break;
case op_literals:
while (--n >= 0) {
blr_indent(control, level);
n2 = PRINT_BYTE;
while (--n2 >= 0)
PRINT_CHAR;
offset = PRINT_LINE;
}
break;
case op_union:
while (--n >= 0) {
PRINT_VERB;
PRINT_VERB;
}
break;
case op_relation:
blr_operator = BLR_BYTE;
blr_print_blr(control, blr_operator);
if (blr_operator != blr_relation && blr_operator != blr_rid)
blr_error(control,
"*** blr_relation or blr_rid must be object of blr_lock_relation, %d found ***",
(int) blr_operator);
if (blr_operator == blr_relation) {
n = PRINT_BYTE;
while (--n >= 0)
PRINT_CHAR;
}
else
PRINT_WORD;
break;
case op_exec_into: {
PRINT_VERB;
if (PRINT_BYTE)
PRINT_VERB;
while (--n >= 0)
PRINT_VERB;
break;
}
default:
fb_assert(FALSE);
break;
}
}
static int blr_print_word(gds_ctl* control)
{
/**************************************
*
* b l r _ p r i n t _ w o r d
*
**************************************
*
* Functional description
* Print a VAX word as a numeric value an return same.
*
**************************************/
UCHAR v1, v2;
v1 = BLR_BYTE;
v2 = BLR_BYTE;
blr_format(control,
(control->ctl_language) ? "chr(%d),chr(%d), " : "%d,%d, ",
(int) v1, (int) v2);
return (v2 << 8) | v1;
}
void gds__cleanup(void)
{
/**************************************
*
* c l e a n u p
*
**************************************
*
* Functional description
* Exit handler for image exit.
*
**************************************/
CLEAN clean;
FPTR_VOID_PTR routine;
void *arg;
#ifdef UNIX
if (gds_pid != getpid())
return;
#endif
gds__msg_close(NULL);
while (clean = cleanup_handlers) {
cleanup_handlers = clean->clean_next;
routine = clean->clean_routine;
arg = clean->clean_arg;
/* We must free the handler before calling it because there
may be a handler (and is) that frees all memory that has
been allocated. */
FREE_LIB_MEMORY(clean);
(*routine)(arg);
}
#ifdef V4_THREADING
/* V4_DESTROY; */
#endif
initialized = 0;
}
static void init(void)
{
/**************************************
*
* i n i t
*
**************************************
*
* Functional description
* Do anything necessary to initialize module/system.
*
**************************************/
#if defined(VMS)
ISC_STATUS status;
#endif
if (initialized)
return;
/* V4_INIT; */
/* V4_GLOBAL_MUTEX_LOCK; */
if (initialized) {
/* V4_GLOBAL_MUTEX_UNLOCK; */
return;
}
#ifdef VMS
exit_description.exit_handler = cleanup;
exit_description.args = 1;
exit_description.arg[0] = &exit_status;
status = sys$dclexh(&exit_description);
#endif
#ifdef UNIX
gds_pid = getpid();
#ifdef SUPERSERVER
#if (defined SOLARIS || defined HPUX || defined LINUX)
{
/* Increase max open files to hard limit for Unix
platforms which are known to have low soft limits. */
struct rlimit old, new_;
if (!getrlimit(RLIMIT_NOFILE, &old) && old.rlim_cur < old.rlim_max) {
new_.rlim_cur = new_.rlim_max = old.rlim_max;
if (!setrlimit(RLIMIT_NOFILE, &new_))
{
#if _FILE_OFFSET_BITS == 64 && defined SOLARIS
gds__log("64 bit i/o support is on.");
gds__log("Open file limit increased from %lld to %lld",
old.rlim_cur, new_.rlim_cur);
#else
gds__log("Open file limit increased from %d to %d",
old.rlim_cur, new_.rlim_cur);
#endif
}
}
}
#endif
#endif /* SUPERSERVER */
#endif /* UNIX */
atexit(gds__cleanup);
initialized = 1;
#ifndef REQUESTER
ISC_signal_init();
#endif
/* V4_GLOBAL_MUTEX_UNLOCK; */
}
static int yday(const struct tm* times)
{
/**************************************
*
* y d a y
*
**************************************
*
* Functional description
* Convert a calendar date to the day-of-year.
*
* The unix time structure considers
* january 1 to be Year day 0, although it
* is day 1 of the month. (Note that QLI,
* when printing Year days takes the other
* view.)
*
**************************************/
SSHORT day = times->tm_mday;
const SSHORT month = times->tm_mon;
const SSHORT year = times->tm_year + 1900;
--day;
day += (214 * month + 3) / 7;
if (month < 2)
return day;
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
--day;
else
day -= 2;
return day;
}
static void ndate(SLONG nday, tm* times)
{
/**************************************
*
* n d a t e
*
**************************************
*
* Functional description
* Convert a numeric day to [day, month, year].
*
* Calenders are divided into 4 year cycles.
* 3 Non-Leap years, and 1 leap year.
* Each cycle takes 365*4 + 1 == 1461 days.
* There is a further cycle of 100 4 year cycles.
* Every 100 years, the normally expected leap year
* is not present. Every 400 years it is.
* This cycle takes 100 * 1461 - 3 == 146097 days
* The origin of the constant 2400001 is unknown.
* The origin of the constant 1721119 is unknown.
* The difference between 2400001 and 1721119 is the
* number of days From 0/0/0000 to our base date of
* 11/xx/1858. (678882)
* The origin of the constant 153 is unknown.
*
* This whole routine has problems with ndates
* less than -678882 (Approx 2/1/0000).
*
**************************************/
SLONG year, month, day;
nday -= 1721119 - 2400001;
const SLONG century = (4 * nday - 1) / 146097;
nday = 4 * nday - 1 - 146097 * century;
day = nday / 4;
nday = (4 * day + 3) / 1461;
day = 4 * day + 3 - 1461 * nday;
day = (day + 4) / 4;
month = (5 * day - 3) / 153;
day = 5 * day - 3 - 153 * month;
day = (day + 5) / 5;
year = 100 * century + nday;
if (month < 10)
month += 3;
else {
month -= 9;
year += 1;
}
times->tm_mday = (int) day;
times->tm_mon = (int) month - 1;
times->tm_year = (int) year - 1900;
}
static GDS_DATE nday(const tm* times)
{
/**************************************
*
* n d a y
*
**************************************
*
* Functional description
* Convert a calendar date to a numeric day
* (the number of days since the base date).
*
**************************************/
const SSHORT day = times->tm_mday;
SSHORT month = times->tm_mon + 1;
SSHORT year = times->tm_year + 1900;
if (month > 2)
month -= 3;
else {
month += 9;
year -= 1;
}
const SLONG c = year / 100;
const SLONG ya = year - 100 * c;
return (GDS_DATE) (((SINT64) 146097 * c) / 4 +
(1461 * ya) / 4 +
(153 * month + 2) / 5 + day + 1721119 - 2400001);
}
static void sanitize(TEXT* locale)
{
/**************************************
*
* s a n i t i z e
*
**************************************
*
* Functional description
* Clean up a locale to make it acceptable for use in file names
* for Windows NT, PC.
* replace any period with '_' for NT or PC.
*
**************************************/
while (*locale) {
if (*locale == '.')
*locale = '_';
locale++;
}
}
static void safe_concat_path(TEXT *resultString, const TEXT *appendString)
{
/**************************************
*
* s a f e _ c o n c a t _ p a t h
*
**************************************
*
* Functional description
* Safely appends appendString to resultString using paths rules.
* resultString must be at least MAXPATHLEN size.
*
**************************************/
int len = strlen(resultString);
if (resultString[len - 1] != PathUtils::dir_sep && len < MAXPATHLEN - 1) {
resultString[len++] = PathUtils::dir_sep;
resultString[len] = 0;
}
int alen = strlen(appendString);
if (len + alen > MAXPATHLEN - 1)
alen = MAXPATHLEN - 1 - len;
fb_assert(alen >= 0);
memcpy(&resultString[len], appendString, alen);
resultString[len + alen] = 0;
}
void gds__default_printer(void* arg, SSHORT offset, const TEXT* line)
{
ib_printf("%4d %s\n", offset, line);
}
void gds__trace_printer(void* arg, SSHORT offset, const TEXT* line)
{
// Assume that line is not too long
char buffer[PRETTY_BUFFER_SIZE+10], *p = buffer;
gds__ulstr(p, offset, 4, ' ');
p += strlen(p);
*p++ = ' ';
strcpy(p, line); p+=strlen(p);
*p++ = '\n';
*p = 0;
gds__trace_raw(buffer);
}
#ifdef DEBUG_GDS_ALLOC
#undef gds__alloc
void* API_ROUTINE gds__alloc(SLONG size)
{
/**************************************
*
* g d s _ $ a l l o c (alternate debug entrypoint)
*
**************************************
*
* Functional description
*
* NOTE: This function should be the last in the file due to
* the undef of gds__alloc above.
*
* For modules not recompiled with DEBUG_GDS_ALLOC, this provides
* an entry point to malloc again.
*
**************************************/
return gds__alloc_debug(size, "-- Unknown --", 0);
}
#endif