8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-25 02:03:03 +01:00
firebird-mirror/src/jrd/gds.cpp
2005-05-16 11:08:14 +00:00

3675 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
*
*/
// 11 Sept 2002 Nickolay Samofatov
// this defined in included dsc2.h
//#define ISC_TIME_SECONDS_PRECISION 10000L
//#define ISC_TIME_SECONDS_PRECISION_SCALE -4
#include "firebird.h"
#include <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/dsc.h"
#include "../jrd/constants.h"
#include "../common/classes/locks.h"
#include "../common/classes/timestamp.h"
#include "../common/classes/init.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#include <errno.h>
#include <stdarg.h>
#include "../jrd/jrd_time.h"
#if defined(WIN_NT)
#include <io.h> // umask, close, lseek, read, open, _sopen
#include <process.h>
#include <sys/types.h>
#include <sys/timeb.h>
#endif
#ifdef VMS
#include <file.h>
#include <perror.h>
#include <descrip.h>
#include <types.h>
#include <stat.h>
#include <rmsdef.h>
#else /* !VMS */
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_FILE_H
#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 "gen/iberror.h"
#include "../jrd/ibase.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 <share.h>
#include "err_proto.h"
#undef leave
#endif /* WIN_NT */
// Number of times to try to generate new name for temporary file
const int MAX_TMPFILE_TRIES = 256;
static char ib_prefix_val[MAXPATHLEN];
static char ib_prefix_lock_val[MAXPATHLEN];
static char ib_prefix_msg_val[MAXPATHLEN];
static char fbTempDir[MAXPATHLEN];
#ifdef EMBEDDED
static char fbEmbeddedRoot[MAXPATHLEN];
#endif
static char *ib_prefix = 0;
static char *ib_prefix_lock = 0;
static char *ib_prefix_msg = 0;
static void gdsPrefixInit();
#include "gen/msgs.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif
const SLONG GENERIC_SQLCODE = -999;
#include "../include/fb_types.h"
#include "../jrd/jrd.h"
#include "../common/utils_proto.h"
using Firebird::TimeStamp;
// This structure is used to parse the firebird.msg file.
struct gds_msg
{
ULONG msg_top_tree;
int msg_file;
USHORT msg_bucket_size;
USHORT msg_levels;
SCHAR msg_bucket[1];
};
// 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 void sanitize(TEXT*);
static void safe_concat_path(TEXT* destbuf, const TEXT* srcbuf);
// New functions that try to be safe.
static SLONG safe_interpret(char* const s, const int bufsize,
const ISC_STATUS** const vector);
/* Generic cleanup handlers */
struct clean
{
struct clean* clean_next;
void (*clean_routine)(void*);
void* clean_arg;
};
typedef clean *CLEAN;
static CLEAN cleanup_handlers = NULL;
static gds_msg* global_default_msg = NULL;
static bool initialized = false;
void* API_ROUTINE gds__alloc_debug(SLONG size_request,
const TEXT* filename,
ULONG lineno)
{
return getDefaultMemoryPool()->allocate_nothrow(size_request, 0
#ifdef DEBUG_GDS_ALLOC
, filename, lineno
#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 BLR_BYTE *(control->ctl_blr)++
#define PUT_BYTE(byte) *(control->ctl_ptr)++ = byte
const int op_line = 1;
const int op_verb = 2;
const int op_byte = 3;
const int op_word = 4;
const int op_pad = 5;
const int op_dtype = 6;
const int op_message = 7;
const int op_literal = 8;
const int op_begin = 9;
const int op_map = 10;
const int op_args = 11;
const int op_union = 12;
const int op_indent = 13;
const int op_join = 14;
const int op_parameters = 15;
const int op_error_handler = 16;
const int op_set_error = 17;
const int op_literals = 18;
const int op_relation = 20;
const int op_exec_into = 21;
const int op_cursor_stmt = 22;
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},
dcl_cursor[] = { op_word, op_line, op_verb, 0},
cursor_stmt[] = { op_cursor_stmt, 0 };
#include "../jrd/blp.h"
const char* const FB_LOCK_ENV = "FIREBIRD_LOCK";
const char* const FB_MSG_ENV = "FIREBIRD_MSG";
const char* const 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*
code_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);
*code_class = GET_CLASS(code);
return GET_CODE(code);
}
void API_ROUTINE isc_decode_date(const ISC_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;
TimeStamp::decode_date(*date, times);
}
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: This routine is intended only for public API use. Engine itself and
* utilities should be using TimeStamp class directly in type-safe manner.
*
**************************************/
Firebird::TimeStamp(*date).decode(static_cast<tm*>(times_arg));
}
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, ISC_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
*
**************************************/
Firebird::TimeStamp temp;
temp.encode(static_cast<const tm*>(times_arg));
*date = (ISC_QUAD&)temp.value();
}
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 = TimeStamp::encode_date((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: This routine is intended only for public API use. Engine itself and
* utilities should be using TimeStamp class directly in type-safe manner.
*
**************************************/
Firebird::TimeStamp temp;
temp.encode(static_cast<const tm*>(times_arg));
*date = temp.value();
}
#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;
}
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.
}
void API_ROUTINE gds_alloc_report(ULONG flags, const char* filter_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
char report_name[MAXPATHLEN];
gds__prefix(report_name, "fbsrvreport.txt");
// Our new facilities don't expose flags for reporting.
const bool used_only = !(flags & ALLOC_verbose);
getDefaultMemoryPool()->print_contents(report_name, used_only, filter_filename);
}
/**
fb_interpret
@brief Buffer overrun-aware version of the old gds__interprete
without the double underscore and without the misspelling.
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;
@param s the output buffer where a human readable version of the error is put
@param bufsize the size of the output buffer
@param vector the input, the address of const pointer to the status vector
that was filled by an API call that reported an error. The function
positions the pointer on the next element of the vector.
**/
SLONG API_ROUTINE fb_interpret(char* s, int bufsize, const ISC_STATUS** vector)
{
return safe_interpret(s, bufsize, 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 fb_interpret
for compliance, to be used inside the engine, too. September, 2003.
November, 2004: We agree that fb_interpret is the new, safe interface.
Both gds__interprete and isc_interprete are deprecated. */
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
* See safe_interpret for details. Now this is a wrapper for that function.
* CVC: Since this routine doesn't get the size of the input buffer,
* it's DEPRECATED and we'll assume the buffer size was twice MAXPATHLEN.
*
**************************************/
return safe_interpret(s, MAXPATHLEN << 1, const_cast<const ISC_STATUS**>(vector));
}
/**
safe_interpret
@brief 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;
@param s the output buffer where a human readable version of the error is put
@param bufsize the size of the output buffer
@param vector the input, the address of const pointer to the status vector
that was filled by an API call that reported an error. The function
positions the pointer on the next element of the vector.
**/
static SLONG safe_interpret(char* const s, const int bufsize,
const ISC_STATUS** const vector)
{
// CVC: It doesn't make sense to provide a buffer smaller than 50 bytes.
// Return error otherwise.
// Also, if the first element of the vector doesn't signal an error, return.
if (!**vector || bufsize < 50)
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];
}
const TEXT* args[10];
const TEXT** arg = args;
const char* const* const argend = arg + FB_NELEM(args);
/* Parse and collect any arguments that may be present */
TEXT* p = 0;
const TEXT* q;
int temp_len = BUFFER_SMALL;
TEXT* temp = NULL;
for (;;)
{
// CVC: Close overflow in "args".
if (arg >= argend)
break;
int len;
const UCHAR x = (UCHAR) *v++;
switch (x)
{
case isc_arg_string:
case isc_arg_number:
*arg++ = (TEXT*) *v++;
continue;
case isc_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) temp_len);
/* FREE: at procedure exit */
if (!temp) /* NOMEM: */
return 0;
}
len = 1 + (int) *v++; // CVC: Add one for the needed terminator.
q = (const TEXT*) *v++;
// ensure that we do not overflow the buffer allocated
len = (temp_len < len) ? temp_len : len;
if (len)
{
// CVC: On the next iteration, we don't have the full buffer
// but only the remainer of the buffer. We decrement here before
// the loop changes "len".
temp_len -= len;
*arg++ = p;
// We'll silently truncate the parameter to our available space.
while (--len) // CVC: Decrement first to make room for the null terminator.
*p++ = *q++;
*p++ = 0;
}
else // No space at all, pass the empty string.
*arg++ = "";
continue;
default:
break;
}
--v;
break;
}
/* Handle primary code on a system by system basis */
switch ((UCHAR) (*vector)[0]) {
case isc_arg_warning:
case isc_arg_gds:
{
USHORT fac = 0, dummy_class = 0;
const ISC_STATUS decoded = gds__decode(code, &fac, &dummy_class);
if (gds__msg_format(0, fac, (USHORT) decoded,
bufsize, s, args[0], args[1], args[2], args[3],
args[4]) < 0)
{
if ((decoded < FB_NELEM(messages) - 1) && (decoded >= 0))
fb_utils::snprintf(s, bufsize, messages[decoded], args[0], args[1], args[2],
args[3], args[4]);
else
fb_utils::snprintf(s, bufsize, "unknown ISC error %ld", code); /* TXNN */
}
}
break;
case isc_arg_interpreted:
//p = s;
q = (const TEXT*) (*vector)[1];
//while ((*p++ = *q++) /*!= NULL*/);
strncpy(s, q, bufsize);
s[bufsize - 1] = 0;
break;
case isc_arg_unix:
/* The strerror() function returns the appropriate description
string, or an unknown error message if the error code is unknown. */
q = (const TEXT*) strerror(code);
strncpy(s, q, bufsize);
s[bufsize - 1] = 0;
break;
case isc_arg_dos:
fb_utils::snprintf(s, bufsize, "unknown dos error %ld", code); /* TXNN */
break;
#ifdef VMS
case isc_arg_vms:
{
SSHORT ll = 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 = bufsize;
desc.dsc$a_pointer = s;
TEXT flags[4];
ISC_STATUS status = sys$getmsg(code, &ll, &desc, 15, flags);
if (status & 1)
s[ll] = 0;
else
sprintf(s, "uninterpreted VMS code %x", code); /* TXNN */
}
break;
#endif
#ifdef WIN_NT
case isc_arg_win32:
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
code,
GetUserDefaultLangID(),
s,
bufsize,
NULL)
&& !FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
code,
0, // TMN: Fallback to system known language
s,
bufsize,
NULL))
{
fb_utils::snprintf(s, bufsize, "unknown Win32 error %ld", code); /* TXNN */
}
break;
#endif
default:
if (temp)
gds__free(temp);
return 0;
}
if (temp)
gds__free(temp);
*vector = v;
const TEXT* end = s;
while (*end)
end++;
return static_cast<SLONG>(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;
}
const int SECS_PER_HOUR = 60 * 60;
const int SECS_PER_DAY = SECS_PER_HOUR * 24;
#ifdef WIN_NT
Firebird::Mutex 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];
// Note: signal-safe code
gds__prefix(name, LOGFILE);
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.
const int days = now / SECS_PER_DAY;
int rem = now % SECS_PER_DAY;
tm today;
TimeStamp::decode_date(days + TimeStamp::GDS_EPOCH_START, &today);
today.tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
today.tm_min = rem / 60;
today.tm_sec = rem % 60;
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;
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);
const int oldmask = umask(0111);
#ifdef WIN_NT
trace_mutex.enter();
#endif
FILE* file = fopen(name, "a");
if (file != NULL)
{
fprintf(file, "\n%s%s\t%.25s\t",
ISC_get_host(name, MAXPATHLEN), gdslogid, ctime(&now));
va_start(ptr, text);
vfprintf(file, text, ptr);
va_end(ptr);
fprintf(file, "\n\n");
fclose(file);
}
#ifdef WIN_NT
trace_mutex.leave();
#endif
umask(oldmask);
}
void API_ROUTINE gds__print_pool(JrdMemoryPool* pool, const TEXT* text, ...)
{
/**************************************
*
* g d s _ p r i n t _ p o o l
*
**************************************
*
* Functional description
* Print pool contents to the log file.
* Preced it with normal log record as in gds__log
*
**************************************/
va_list ptr;
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);
const int oldmask = umask(0111);
#ifdef WIN_NT
trace_mutex.enter();
#endif
FILE* file = fopen(name, "a");
if (file != NULL)
{
fprintf(file, "\n%s%s\t%.25s\t",
ISC_get_host(name, MAXPATHLEN), gdslogid, ctime(&now));
va_start(ptr, text);
vfprintf(file, text, ptr);
va_end(ptr);
fprintf(file, "\n");
pool->print_contents(file, false);
fprintf(file, "\n");
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 = database ? strlen(database) : 0;
fb_assert(db_len < BUFFER_XLARGE - 2);
fb_assert(db_len < MAXPATHLEN);
#endif
TEXT* p = buffer;
const TEXT* const end = p + BUFFER_XLARGE;
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 && safe_interpret(p, end - p, &status_vector));
p[-2] = 0;
gds__log(buffer, 0);
gds__free(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* messageL = static_cast<gds_msg*>(handle);
if (!messageL) {
if (!global_default_msg) {
return 0;
}
messageL = global_default_msg;
}
global_default_msg = NULL;
const int fd = messageL->msg_file;
FREE_LIB_MEMORY(messageL);
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.
*
**************************************/
SLONG 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;
TEXT* 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. */
const int n = gds__msg_lookup(handle, facility, number, length, buffer, NULL);
if (n > 0 && n < length)
{
fb_utils::snprintf(formatted, size, buffer, arg1, arg2, arg3, arg4, arg5);
}
else
{
Firebird::string s;
s.printf("can't format message %d:%d -- ", facility, number);
if (n == -1)
s += "message text not found";
else if (n == -2) {
s += "message file ";
gds__prefix_msg(formatted, MSG_FILE);
s += formatted;
s += " not found";
}
else {
fb_utils::snprintf(formatted, size, "message system code %d", n);
s += formatted;
}
s.copyTo(formatted, size);
}
const USHORT l = strlen(formatted);
const TEXT* const end = buffer + length - 1;
for (const TEXT* p = formatted; *p && buffer < end;) {
*buffer++ = *p++;
}
*buffer = 0;
gds__free(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.
*
**************************************/
// Handle default message file
int status = -1;
gds_msg* messageL = (gds_msg*) handle;
if (!messageL && !(messageL = global_default_msg)) {
/* Try environment variable setting first */
TEXT* p = getenv("ISC_MSGS");
if (p == NULL ||
(status =
gds__msg_open(reinterpret_cast<void**>(&messageL), p)))
{
TEXT translated_msg_file[sizeof(MSG_FILE_LANG) + LOCALE_MAX + 1];
/* Try declared language of this attachment */
/* This is not quite the same as the language declared on the
READY statement */
TEXT* 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); // CVC: Sanitizing environment variable???
fb_utils::snprintf(translated_msg_file,
sizeof(translated_msg_file), MSG_FILE_LANG, p);
gds__prefix_msg(msg_file, translated_msg_file);
status =
gds__msg_open(reinterpret_cast<void**>(&messageL),
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**>(&messageL),
msg_file);
}
gds__free(msg_file);
}
if (status)
return status;
global_default_msg = messageL;
}
/* Search down index levels to the leaf. If we get lost, punt */
const ULONG code = MSG_NUMBER(facility, number);
const msgnod* const end =
(msgnod*) ((char*) messageL->msg_bucket + messageL->msg_bucket_size);
ULONG position = messageL->msg_top_tree;
status = 0;
for (USHORT n = 1; !status; n++) {
if (lseek(messageL->msg_file, LSEEK_OFFSET_CAST position, 0) < 0)
status = -6;
else if (read(messageL->msg_file, messageL->msg_bucket,
messageL->msg_bucket_size) < 0)
status = -7;
else if (n == messageL->msg_levels)
break;
else {
for (const msgnod* node = (msgnod*) messageL->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 (const msgrec* leaf = (msgrec*) messageL->msg_bucket; !status;
leaf = NEXT_LEAF(leaf))
{
if (leaf >= (const 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 */
const USHORT 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.
*
**************************************/
const int n = open(filename, O_RDONLY | O_BINARY, 0);
if (n < 0)
return -2;
isc_msghdr header;
if (read(n, &header, sizeof(header)) < 0) {
close(n);
return -3;
}
// Trick to silence compiler when MSG_MINOR_VERSION == 0
const USHORT minor = MSG_MINOR_VERSION;
if (header.msghdr_major_version != MSG_MAJOR_VERSION ||
header.msghdr_minor_version < minor)
{
close(n);
return -4;
}
gds_msg* messageL =
(gds_msg*) ALLOC_LIB_MEMORY((SLONG) sizeof(gds_msg) +
header.msghdr_bucket_size - 1);
/* FREE: in gds__msg_close */
if (!messageL) { /* 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 *) messageL);
#endif
messageL->msg_file = n;
messageL->msg_bucket_size = header.msghdr_bucket_size;
messageL->msg_levels = header.msghdr_levels;
messageL->msg_top_tree = header.msghdr_top_tree;
*handle = messageL;
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[BUFFER_MEDIUM];
gds__msg_format(handle, facility, number, sizeof(formatted),
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
**************************************/
int count = 0;
if (arg_type < IB_PREFIX_TYPE || arg_type > IB_PREFIX_MSG_TYPE)
return ((SLONG) - 1);
gdsPrefixInit();
char* prefix_ptr;
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.
*
**************************************/
resultString[0] = 0;
gdsPrefixInit();
strcpy(resultString, ib_prefix); // safe - no BO
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.
*
**************************************/
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. */
TEXT temp[256];
if (ISC_expand_logical_once(ISC_LOGICAL, sizeof(ISC_LOGICAL) - 2, temp, sizeof(temp)))
{
strcpy(string, ISC_LOGICAL);
strcat(string, root);
return;
}
TEXT* p = temp;
for (const TEXT* q = root; *p = UPPER7(*q); q++)
if (*p++ == ']')
break;
const SSHORT len = p - temp;
for (const isc_vms_prefix* 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.
*
**************************************/
string[0] = 0;
gdsPrefixInit();
#ifdef EMBEDDED
char buf[MAXPATHLEN];
fb_utils::snprintf(buf, MAXPATHLEN, root, fbEmbeddedRoot);
root = buf;
#endif
strcpy(string, ib_prefix_lock); // safe - no BO
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.
*
*************************************************/
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. */
TEXT temp[256];
if (ISC_expand_logical_once
(ISC_LOGICAL_LOCK, sizeof(ISC_LOGICAL_LOCK) - 2, temp, sizeof(temp)))
{
strcpy(string, ISC_LOGICAL_LOCK);
strcat(string, root);
return;
}
TEXT* p = temp;
for (const TEXT* q = root; *p = UPPER7(*q); q++)
if (*p++ == ']')
break;
const SSHORT len = p - temp;
for (const isc_vms_prefix* 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;
gdsPrefixInit();
strcpy(string, ib_prefix_msg); // safe - no BO
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.
*
*************************************************/
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. */
TEXT temp[256];
if (ISC_expand_logical_once
(ISC_LOGICAL_MSG, sizeof(ISC_LOGICAL_MSG) - 2, temp, sizeof(temp)))
{
strcpy(string, ISC_LOGICAL_MSG);
strcat(string, root);
return;
}
TEXT* p = temp;
for (const TEXT* q = root; *p = UPPER7(*q); q++)
if (*p++ == ']')
break;
const SSHORT len = p - temp;
for (const isc_vms_prefix* 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] == isc_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 (!safe_interpret(s, BUFFER_LARGE, &vector)) {
gds__free(s);
return vec[1];
}
gds__put_error(s);
s[0] = '-';
while (safe_interpret(s + 1, BUFFER_LARGE - 1, &vector))
gds__put_error(s);
gds__free(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 = 0;
*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++ != isc_bpb_version1)
return type;
while (p < end) {
const UCHAR op = *p++;
const USHORT length = *p++;
switch (op) {
case isc_bpb_source_type:
*source = (USHORT) gds__vax_integer(p, length);
break;
case isc_bpb_target_type:
*target = (USHORT) gds__vax_integer(p, length);
break;
case isc_bpb_type:
type = (USHORT) gds__vax_integer(p, length);
break;
case isc_bpb_source_interp:
if (source_interp)
*source_interp = (USHORT) gds__vax_integer(p, length);
break;
case isc_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* fieldL,
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 {
*fieldL++ = *string++;
} while (--l);
if (fill > 0)
do {
*fieldL++ = ' ';
} 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;
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,");
SSHORT level = 0;
SLONG offset = 0;
blr_print_line(control, (SSHORT) offset);
blr_print_verb(control, level);
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");
blr_print_line(control, (SSHORT) offset);
} // 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
struct dsc$descriptor_s desc;
ISC_make_desc(string, &desc, 0);
lib$put_output(&desc);
#else
fputs(string, stderr);
fputc('\n', stderr);
fflush(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.
*
**************************************/
*((ISC_QUAD*) quad_out) = *((ISC_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.
*/
if (!initialized)
init();
CLEAN 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 != isc_arg_end)
{
if (*s == isc_arg_gds)
{
s++;
if (*s == isc_sqlerr)
{
return *(s + 2);
}
if (!have_sqlcode) {
/* Now check the hard-coded mapping table of gds_codes to
sql_codes */
USHORT fac = 0, dummy_class = 0;
const USHORT code = (USHORT) gds__decode(status_vector[1], &fac, &dummy_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 == isc_arg_cstring)
s += 3; /* skip: isc_arg_cstring <len> <ptr> */
else
s += 2; /* skip: isc_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.
*
**************************************/
buffer[0] = 0;
gdsPrefixInit();
strcpy(buffer, fbTempDir); // safe - no BO
}
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 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];
const 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 = 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 *)(IPTR)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 = fdopen((int)(IPTR)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;
for (CLEAN* 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* ib_ext_lib_path = getenv(ib_env_var);
if (!ib_ext_lib_path) {
strncpy(resolved_module, module, length);
return TRUE; /* The variable is not defined. Return TRUE */
}
TEXT abs_module[MAXPATHLEN];
if (EXPAND_PATH(module, abs_module)) {
/* Extract the path from the absolute module name */
const TEXT* q = NULL;
for (const TEXT* mp = abs_module; *mp; mp++)
if ((*mp == '\\') || (*mp == '/'))
q = mp;
TEXT abs_module_path[MAXPATHLEN];
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 TRUE. If it does not find it, then
the module path is not valid so return FALSE */
TEXT abs_path[MAXPATHLEN];
TEXT path[MAXPATHLEN];
// CVC: Warning, strtok is changing the environment variable!
// If this function is called again, only the first substring will be found.
// Multiple engines in the same machine => shudder.
const TEXT* token = strtok(ib_ext_lib_path, ";");
while (token != NULL) {
strcpy(path, token);
/* make sure that there is no traing slash on the path */
TEXT* 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, sizeof(path))))
return TRUE;
if (ISC_expand_logical_once(module, strlen(module) - 2, abs_module, sizeof(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 = 0;
SSHORT shift = 0;
while (--length >= 0) {
value += ((SLONG) * ptr++) << shift;
shift += 8;
}
return value;
}
void API_ROUTINE gds__vtof(
const SCHAR* string,
SCHAR* fieldL, 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) {
*fieldL++ = *string++;
if (--length <= 0)
return;
}
if (length > 0)
do {
*fieldL++ = ' ';
} while (--length);
}
void API_ROUTINE gds__vtov(const SCHAR* string, char* fieldL, 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 ((*fieldL++ = *string++) != 0)
if (--length <= 0) {
*fieldL = 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];
sprintf(error_buffer, "SQLCODE: %d\nSQL ERROR:\n", sqlcode);
TEXT* p = error_buffer;
while (*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.
*
**************************************/
const 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);
va_end(args);
offset = 0;
blr_print_line(control, (SSHORT) offset);
Firebird::status_exception::raise();
}
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);
va_end(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 blr_operator)
{
/**************************************
*
* b l r _ p r i n t _ b l r
*
**************************************
*
* Functional description
* Print a blr item.
*
**************************************/
const char* p;
if (blr_operator > FB_NELEM(blr_table) ||
!(p = blr_table[blr_operator].blr_string))
{
blr_error(control, "*** blr operator %d is undefined ***",
(int) blr_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.
*
**************************************/
const UCHAR 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;
v = c = BLR_BYTE;
const bool 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.
*
**************************************/
SSHORT n;
const USHORT ctype = BLR_BYTE;
switch (ctype) {
case blr_gds_code:
blr_format(control, "blr_gds_code, ");
n = blr_print_byte(control);
while (--n >= 0)
blr_print_char(control);
break;
case blr_exception:
blr_format(control, "blr_exception, ");
n = blr_print_byte(control);
while (--n >= 0)
blr_print_char(control);
break;
case blr_sql_code:
blr_format(control, "blr_sql_code, ");
blr_print_word(control);
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.
*
**************************************/
SSHORT length;
const USHORT dtype = BLR_BYTE;
/* Special case blob (261) to keep down the size of the
jump table */
const TEXT* string;
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 */
const UCHAR v1 = *(control->ctl_blr);
const UCHAR 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 = blr_print_word(control);
break;
case blr_varying:
length = blr_print_word(control) + 2;
break;
case blr_short:
case blr_long:
case blr_quad:
case blr_int64:
blr_print_byte(control);
break;
case blr_text2:
blr_print_word(control);
length = blr_print_word(control);
break;
case blr_varying2:
blr_print_word(control);
length = blr_print_word(control) + 2;
break;
case blr_cstring2:
blr_print_word(control);
length = blr_print_word(control);
break;
default:
if (dtype == blr_cstring)
length = blr_print_word(control);
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.
*
**************************************/
const TEXT *string;
const USHORT 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.
*
**************************************/
SLONG 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, ");
blr_print_line(control, (SSHORT) offset);
return;
}
blr_print_blr(control, blr_operator);
level++;
const UCHAR* ops = blr_table[blr_operator].blr_operators;
SSHORT n;
while (*ops)
switch (*ops++) {
case op_verb:
blr_print_verb(control, level);
break;
case op_line:
offset = blr_print_line(control, (SSHORT) offset);
break;
case op_byte:
n = blr_print_byte(control);
break;
case op_word:
n = blr_print_word(control);
break;
case op_pad:
PUT_BYTE(' ');
break;
case op_dtype:
n = blr_print_dtype(control);
break;
case op_literal:
while (--n >= 0)
blr_print_char(control);
break;
case op_join:
blr_print_join(control);
break;
case op_message:
while (--n >= 0) {
blr_indent(control, level);
blr_print_dtype(control);
offset = blr_print_line(control, (SSHORT) offset);
}
break;
case op_parameters:
level++;
while (--n >= 0)
blr_print_verb(control, level);
level--;
break;
case op_error_handler:
while (--n >= 0) {
blr_indent(control, level);
blr_print_cond(control);
offset = blr_print_line(control, (SSHORT) offset);
}
break;
case op_set_error:
blr_print_cond(control);
break;
case op_indent:
blr_indent(control, level);
break;
case op_begin:
while ((SCHAR) * (control->ctl_blr) != (SCHAR) blr_end)
blr_print_verb(control, level);
break;
case op_map:
while (--n >= 0) {
blr_indent(control, level);
blr_print_word(control);
offset = blr_print_line(control, (SSHORT) offset);
blr_print_verb(control, level);
}
break;
case op_args:
while (--n >= 0)
blr_print_verb(control, level);
break;
case op_literals:
while (--n >= 0) {
blr_indent(control, level);
SSHORT n2 = blr_print_byte(control);
while (--n2 >= 0)
blr_print_char(control);
offset = blr_print_line(control, (SSHORT) offset);
}
break;
case op_union:
while (--n >= 0) {
blr_print_verb(control, level);
blr_print_verb(control, level);
}
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 = blr_print_byte(control);
while (--n >= 0)
blr_print_char(control);
}
else
blr_print_word(control);
break;
case op_exec_into: {
blr_print_verb(control, level);
if (! blr_print_byte(control)) {
blr_print_verb(control, level);
}
while (n-- > 0) {
blr_print_verb(control, level);
}
break;
}
case op_cursor_stmt: {
blr_operator = blr_print_byte(control);
blr_print_word(control);
if (blr_operator == blr_cursor_fetch) {
#ifdef SCROLLABLE_CURSORS
if (BLR_PEEK == blr_seek) {
blr_print_verb(control, level);
}
#endif
}
offset = blr_print_line(control, (SSHORT) offset);
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.
*
**************************************/
const UCHAR v1 = BLR_BYTE;
const UCHAR 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.
*
**************************************/
#ifdef UNIX
if (gds_pid != getpid())
return;
#endif
gds__msg_close(NULL);
CLEAN clean;
while (clean = cleanup_handlers) {
cleanup_handlers = clean->clean_next;
FPTR_VOID_PTR routine = clean->clean_routine;
void* 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 = false;
}
static void init(void)
{
/**************************************
*
* i n i t
*
**************************************
*
* Functional description
* Do anything necessary to initialize module/system.
*
**************************************/
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;
ISC_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;
if (!getrlimit(RLIMIT_NOFILE, &old) && old.rlim_cur < old.rlim_max) {
struct rlimit new_max;
new_max.rlim_cur = new_max.rlim_max = old.rlim_max;
if (!setrlimit(RLIMIT_NOFILE, &new_max))
{
#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_max.rlim_cur);
#else
gds__log("Open file limit increased from %d to %d",
old.rlim_cur, new_max.rlim_cur);
#endif
}
}
}
#endif
#endif /* SUPERSERVER */
#endif /* UNIX */
atexit(gds__cleanup);
initialized = true;
gdsPrefixInit();
#ifndef REQUESTER
ISC_signal_init();
#endif
/* V4_GLOBAL_MUTEX_UNLOCK; */
}
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 most MAXPATHLEN size.
* Thread/signal safe code.
*
**************************************/
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)
{
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];
char* 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);
}
#undef gds__alloc
void* API_ROUTINE gds__alloc(SLONG size_request)
{
return getDefaultMemoryPool()->allocate_nothrow(size_request, 0
#ifdef DEBUG_GDS_ALLOC
, __FILE__, __LINE__
#endif
);
}
class InitPrefix
{
public:
static void init()
{
// Get ib_prefix value from config file
Firebird::PathName prefix(Config::getRootDirectory());
if (prefix.isEmpty())
{
prefix = FB_PREFIX;
}
prefix.copyTo(ib_prefix_val, sizeof(ib_prefix_val));
ib_prefix = ib_prefix_val;
// Find appropiate temp directory
const char* tempDir = getenv(FB_TMP_ENV);
if (!tempDir)
{
#ifdef WIN_NT
const DWORD len = GetTempPath(sizeof(fbTempDir), fbTempDir);
// This checks "TEMP" and "TMP" environment variables
if (len && len < sizeof(fbTempDir))
{
tempDir = fbTempDir;
}
#else
tempDir = getenv("TMP");
#endif
}
if (!tempDir || strlen(tempDir) >= MAXPATHLEN)
{
tempDir = WORKFILE;
}
strcpy(fbTempDir, tempDir);
#ifdef EMBEDDED
// Generate filename based on the current PID
Firebird::PathName buf;
buf.printf(FB_PID_FILE, getpid());
buf.copyTo(fbEmbeddedRoot, sizeof(fbEmbeddedRoot));
#endif
// Find appropriate Firebird lock file prefix
// Override conditional defines with the enviroment
// variable FIREBIRD_LOCK if it is set.
Firebird::PathName lockPrefix(getenv(FB_LOCK_ENV) ? getenv(FB_LOCK_ENV) : "");
if (lockPrefix.isEmpty())
{
#ifdef EMBEDDED
lockPrefix = tempDir;
#else
lockPrefix = prefix;
#endif
}
lockPrefix.copyTo(ib_prefix_lock_val, sizeof(ib_prefix_lock_val));
ib_prefix_lock = ib_prefix_lock_val;
// Find appropriate Firebird message file prefix.
Firebird::PathName msgPrefix(getenv(FB_MSG_ENV) ? getenv(FB_MSG_ENV) : "");
if (msgPrefix.isEmpty())
{
msgPrefix = prefix;
}
msgPrefix.copyTo(ib_prefix_msg_val, sizeof(ib_prefix_msg_val));
ib_prefix_msg = ib_prefix_msg_val;
}
static void cleanup()
{
}
};
static Firebird::InitMutex<InitPrefix> initPrefix;
static void gdsPrefixInit()
{
/**************************************
*
* g d s P r e f i x I n i t
*
**************************************
*
* Functional description
* Initialize all data in various ib_prefixes.
* Calling it before any signal can be caught (from init())
* makes gds__prefix* family of functions signal-safe.
* In order not to break external API, call to gdsPrefixInit
* must be present in all gds__prefix functions. Due to correct
* init this doesn't make them unsafe.
*
**************************************/
initPrefix.init();
}