mariadb/storage/connect/plgdbutl.cpp
2023-07-20 11:54:52 +02:00

1614 lines
51 KiB
C++

/********** PlgDBUtl Fpe C++ Program Source Code File (.CPP) ***********/
/* PROGRAM NAME: PLGDBUTL */
/* ------------- */
/* Version 4.1 */
/* */
/* COPYRIGHT: */
/* ---------- */
/* (C) Copyright to the author Olivier BERTRAND 1998-2020 */
/* */
/* WHAT THIS PROGRAM DOES: */
/* ----------------------- */
/* Utility functions used by DB semantic routines. */
/* */
/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
/* -------------------------------------- */
/* */
/* REQUIRED FILES: */
/* --------------- */
/* See Readme.C for a list and description of required SYSTEM files. */
/* */
/* PLGDBUTL.C - Source code */
/* GLOBAL.H - Global declaration file */
/* PLGDBSEM.H - DB application declaration file */
/* */
/* REQUIRED LIBRARIES: */
/* ------------------- */
/* OS2.LIB - OS2 libray */
/* LLIBCE.LIB - Protect mode/standard combined large model C */
/* library */
/* */
/* REQUIRED PROGRAMS: */
/* ------------------ */
/* IBM, MS, Borland or GNU C++ Compiler */
/* IBM, MS, Borland or GNU Linker */
/***********************************************************************/
/***********************************************************************/
/* Include relevant MariaDB header file. */
/***********************************************************************/
#include "my_global.h"
#include "my_pthread.h"
#if defined(_WIN32)
#include <io.h>
#include <fcntl.h>
#include <errno.h>
#define BIGMEM 1048576 // 1 Megabyte
#else // !_WIN32
#include <unistd.h>
#include <fcntl.h>
//#if defined(THREAD)
#include <pthread.h>
//#endif // THREAD
#include <stdarg.h>
#define BIGMEM 2147483647 // Max int value
#endif // !_WIN32
#include <locale.h>
/***********************************************************************/
/* Include application header files */
/***********************************************************************/
#include "global.h" // header containing all global declarations.
#include "plgdbsem.h" // header containing the DB applic. declarations.
#include "preparse.h" // For DATPAR
#include "osutil.h"
#include "maputil.h"
#include "catalog.h"
#include "colblk.h"
#include "xtable.h" // header of TBX, TDB and TDBASE classes
#include "tabcol.h" // header of XTAB and COLUMN classes
#include "valblk.h"
#include "rcmsg.h"
#ifdef ZIP_SUPPORT
#include "filamzip.h"
#endif // ZIP_SUPPORT
#ifdef JAVA_SUPPORT
#include "javaconn.h"
#endif // JAVA_SUPPORT
#ifdef CMGO_SUPPORT
#include "cmgoconn.h"
#endif // JAVA_SUPPORT
/***********************************************************************/
/* DB static variables. */
/***********************************************************************/
bool Initdone = false;
bool plugin = false; // True when called by the XDB plugin handler
extern "C" {
extern char version[];
} // extern "C"
//#if defined(_WIN32)
//extern CRITICAL_SECTION parsec; // Used calling the Flex parser
//#else // !_WIN32
extern pthread_mutex_t parmut;
//#endif // !_WIN32
// The debug trace used by the main thread
FILE *pfile = NULL;
MBLOCK Nmblk = {NULL, false, 0, false, NULL}; // Used to init MBLOCK's
/***********************************************************************/
/* Routines called externally and internally by utility routines. */
/***********************************************************************/
bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
bool EvalLikePattern(LPCSTR, LPCSTR);
void PlugConvertConstant(PGLOBAL, void* &, short&);
#ifdef DOMDOC_SUPPORT
void CloseXMLFile(PGLOBAL, PFBLOCK, bool);
#endif // DOMDOC_SUPPORT
#ifdef LIBXML2_SUPPORT
#include "libdoc.h"
#endif // LIBXML2_SUPPORT
#ifdef ODBC_SUPPORT
void OdbcClose(PGLOBAL g, PFBLOCK fp);
#endif // ODBC_SUPPORT
/***********************************************************************/
/* Routines for file IO with error reporting to g->Message */
/* Note: errno and strerror must be called before the message file */
/* is read in the case of XMSG compile. */
/***********************************************************************/
static void global_open_error_msg(GLOBAL *g, int msgid, const char *path,
const char *mode)
{
int len, rno= (int)errno;
char errmsg[256]= "";
strncat(errmsg, strerror(errno), 255);
switch (msgid)
{
case MSGID_CANNOT_OPEN:
len= snprintf(g->Message, sizeof(g->Message) - 1,
MSG(CANNOT_OPEN), // Cannot open %s
path);
break;
case MSGID_OPEN_MODE_ERROR:
len= snprintf(g->Message, sizeof(g->Message) - 1,
MSG(OPEN_MODE_ERROR), // "Open(%s) error %d on %s"
mode, rno, path);
break;
case MSGID_OPEN_MODE_STRERROR:
{char fmt[256];
strcat(strcpy(fmt, MSG(OPEN_MODE_ERROR)), ": %s");
len= snprintf(g->Message, sizeof(g->Message) - 1,
fmt, // Open(%s) error %d on %s: %s
mode, rno, path, errmsg);
}break;
case MSGID_OPEN_STRERROR:
len= snprintf(g->Message, sizeof(g->Message) - 1,
MSG(OPEN_STRERROR), // "open error: %s"
errmsg);
break;
case MSGID_OPEN_ERROR_AND_STRERROR:
len= snprintf(g->Message, sizeof(g->Message) - 1,
//OPEN_ERROR does not work, as it wants mode %d (not %s)
//MSG(OPEN_ERROR) "%s",// "Open error %d in mode %d on %s: %s"
"Open error %d in mode %s on %s: %s",
rno, mode, path, errmsg);
break;
case MSGID_OPEN_EMPTY_FILE:
len= snprintf(g->Message, sizeof(g->Message) - 1,
MSG(OPEN_EMPTY_FILE), // "Opening empty file %s: %s"
path, errmsg);
break;
default:
DBUG_ASSERT(0);
/* Fall through*/
case 0:
len= 0;
}
g->Message[len]= '\0';
}
FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode)
{
FILE *f;
if (!(f= fopen(path, mode)))
global_open_error_msg(g, msgid, path, mode);
return f;
}
int global_open(GLOBAL *g, int msgid, const char *path, int flags)
{
int h;
if ((h= open(path, flags)) <= 0)
global_open_error_msg(g, msgid, path, "");
return h;
}
int global_open(GLOBAL *g, int msgid, const char *path, int flags, int mode)
{
int h;
if ((h= open(path, flags, mode)) <= 0)
{
char modestr[64];
snprintf(modestr, sizeof(modestr), "%d", mode);
global_open_error_msg(g, msgid, path, modestr);
}
return h;
}
DllExport void SetTrc(void)
{
// If tracing is on, debug must be initialized.
debug = pfile;
} // end of SetTrc
/**************************************************************************/
/* SubAllocate the result structure that will contain result data. */
/**************************************************************************/
PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
int *buftyp, XFLD *fldtyp,
unsigned int *length, bool blank, bool nonull)
{
char cname[NAM_LEN+1];
int i;
PCOLRES *pcrp, crp;
PQRYRES qrp;
try {
/**********************************************************************/
/* Allocate the structure used to contain the result set. */
/**********************************************************************/
qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
pcrp = &qrp->Colresp;
qrp->Continued = false;
qrp->Truncated = false;
qrp->Info = false;
qrp->Suball = true;
qrp->Maxres = maxres;
qrp->Maxsize = 0;
qrp->Nblin = 0;
qrp->Nbcol = 0; // will be ncol
qrp->Cursor = 0;
qrp->BadLines = 0;
for (i = 0; i < ncol; i++) {
*pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
crp = *pcrp;
pcrp = &crp->Next;
memset(crp, 0, sizeof(COLRES));
crp->Colp = NULL;
crp->Ncol = ++qrp->Nbcol;
crp->Type = buftyp[i];
crp->Length = length[i];
crp->Clen = GetTypeSize(crp->Type, length[i]);
crp->Prec = 0;
if (ids > 0) {
#if defined(XMSG)
// Get header from message file
strncpy(cname, PlugReadMessage(g, ids + crp->Ncol, NULL), NAM_LEN);
cname[NAM_LEN] = 0; // for truncated long names
#else // !XMSG
GetRcString(ids + crp->Ncol, cname, sizeof(cname));
#endif // !XMSG
crp->Name = (PSZ)PlugDup(g, cname);
} else
crp->Name = NULL; // Will be set by caller
if (fldtyp)
crp->Fld = fldtyp[i];
else
crp->Fld = FLD_NO;
// Allocate the Value Block that will contain data
if (crp->Length || nonull)
crp->Kdata = AllocValBlock(g, NULL, crp->Type, maxres,
crp->Length, 0, true, blank, false);
else
crp->Kdata = NULL;
if (trace(1))
htrc("Column(%d) %s type=%d len=%d value=%p\n",
crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata);
} // endfor i
*pcrp = NULL;
} catch (int n) {
htrc("Exception %d: %s\n", n, g->Message);
qrp = NULL;
} catch (const char *msg) {
strcpy(g->Message, msg);
htrc("%s\n", g->Message);
qrp = NULL;
} // end catch
return qrp;
} // end of PlgAllocResult
/***********************************************************************/
/* Allocate and initialize the new DB User Block. */
/***********************************************************************/
PDBUSER PlgMakeUser(PGLOBAL g)
{
PDBUSER dbuserp;
if (!(dbuserp = (PDBUSER)malloc(sizeof(DBUSERBLK)))) {
snprintf(g->Message, sizeof(g->Message), MSG(MALLOC_ERROR), "PlgMakeUser");
return NULL;
} // endif dbuserp
memset(dbuserp, 0, sizeof(DBUSERBLK));
dbuserp->Maxbmp = MAXBMP;
//dbuserp->UseTemp = TMP_AUTO;
dbuserp->Check = CHK_ALL;
strcpy(dbuserp->Server, "CONNECT");
return dbuserp;
} // end of PlgMakeUser
/***********************************************************************/
/* PlgGetUser: returns DBUSER block pointer. */
/***********************************************************************/
PDBUSER PlgGetUser(PGLOBAL g)
{
PDBUSER dup = (PDBUSER)((g->Activityp) ? g->Activityp->Aptr : NULL);
if (!dup)
strcpy(g->Message, MSG(APPL_NOT_INIT));
return dup;
} // end of PlgGetUser
/***********************************************************************/
/* PlgGetCatalog: returns CATALOG class pointer. */
/***********************************************************************/
PCATLG PlgGetCatalog(PGLOBAL g, bool jump)
{
PDBUSER dbuserp = PlgGetUser(g);
PCATLG cat = (dbuserp) ? dbuserp->Catalog : NULL;
if (!cat && jump) {
// Raise exception so caller doesn't have to check return value
strcpy(g->Message, MSG(NO_ACTIVE_DB));
throw 1;
} // endif cat
return cat;
} // end of PlgGetCatalog
#if 0
/***********************************************************************/
/* PlgGetDataPath: returns the default data path. */
/***********************************************************************/
char *PlgGetDataPath(PGLOBAL g)
{
PCATLG cat = PlgGetCatalog(g, false);
return (cat) ? cat->GetDataPath() : NULL;
} // end of PlgGetDataPath
#endif // 0
/***********************************************************************/
/* This function returns a database path. */
/***********************************************************************/
char *SetPath(PGLOBAL g, const char *path)
{
char *buf= NULL;
if (path) {
size_t len = strlen(path) + (*path != '.' ? 4 : 1);
if (!(buf = (char*)PlgDBSubAlloc(g, NULL, len)))
return NULL;
if (PlugIsAbsolutePath(path)) {
snprintf(buf, len, "%s", path);
return buf;
} // endif path
if (*path != '.') {
#if defined(_WIN32)
const char *s = "\\";
#else // !_WIN32
const char *s = "/";
#endif // !_WIN32
snprintf(buf, len, ".%s%s%s", s, path, s);
} else
snprintf(buf, len, "%s", path);
} // endif path
return buf;
} // end of SetPath
/***********************************************************************/
/* Extract from a path name the required component. */
/* This function assumes there is enough space in the buffer. */
/***********************************************************************/
char *ExtractFromPath(PGLOBAL g, char *pBuff, char *FileName, OPVAL op)
{
char *drive = NULL, *direc = NULL, *fname = NULL, *ftype = NULL;
switch (op) { // Determine which part to extract
#if defined(_WIN32)
case OP_FDISK: drive = pBuff; break;
#endif // !UNIX
case OP_FPATH: direc = pBuff; break;
case OP_FNAME: fname = pBuff; break;
case OP_FTYPE: ftype = pBuff; break;
default:
snprintf(g->Message, sizeof(g->Message), MSG(INVALID_OPER), op, "ExtractFromPath");
return NULL;
} // endswitch op
// Now do the extraction
_splitpath(FileName, drive, direc, fname, ftype);
return pBuff;
} // end of PlgExtractFromPath
#ifdef NOT_USED
/***********************************************************************/
/* Check the occurrence and matching of a pattern against a string. */
/* Because this function is only used for catalog name checking, */
/* it must be case insensitive. */
/***********************************************************************/
static bool PlugCheckPattern(PGLOBAL g, LPCSTR string, LPCSTR pat)
{
if (pat && strlen(pat)) {
// This leaves 2048 bytes (MAX_STR / 2) for each components
LPSTR name = g->Message + MAX_STR / 2;
strlwr(strcpy(name, string));
strlwr(strcpy(g->Message, pat)); // Can be modified by Eval
return EvalLikePattern(name, g->Message);
} else
return true;
} // end of PlugCheckPattern
#endif /* NOT_USED */
/***********************************************************************/
/* PlugEvalLike: evaluates a LIKE clause. */
/* Syntaxe: M like P escape C. strg->M, pat->P, C not implemented yet */
/***********************************************************************/
bool PlugEvalLike(PGLOBAL g, LPCSTR strg, LPCSTR pat, bool ci)
{
char *tp, *sp;
bool b;
if (trace(2))
htrc("LIKE: strg='%s' pattern='%s'\n", strg, pat);
if (ci) { /* Case insensitive test */
if (strlen(pat) + strlen(strg) + 1 < MAX_STR)
tp = g->Message;
else if (!(tp = new char[strlen(pat) + strlen(strg) + 2])) {
strcpy(g->Message, MSG(NEW_RETURN_NULL));
throw (int)OP_LIKE;
} /* endif tp */
sp = tp + strlen(pat) + 1;
strlwr(strcpy(tp, pat)); /* Make a lower case copy of pat */
strlwr(strcpy(sp, strg)); /* Make a lower case copy of strg */
} else { /* Case sensitive test */
if (strlen(pat) < MAX_STR) /* In most of the case for small pat */
tp = g->Message; /* Use this as temporary work space. */
else if (!(tp = new char[strlen(pat) + 1])) {
strcpy(g->Message, MSG(NEW_RETURN_NULL));
throw (int)OP_LIKE;
} /* endif tp */
strcpy(tp, pat); /* Make a copy to be worked into */
sp = (char*)strg;
} /* endif ci */
b = EvalLikePattern(sp, tp);
if (tp != g->Message) /* If working space was obtained */
delete [] tp; /* by the use of new, delete it. */
return (b);
} /* end of PlugEvalLike */
/***********************************************************************/
/* M and P are variable length character string. If M and P are zero */
/* length strings then the Like predicate is true. */
/* */
/* The Like predicate is true if: */
/* */
/* 1- A subtring of M is a sequence of 0 or more contiguous <CR> of M */
/* and each <CR> of M is part of exactly one substring. */
/* */
/* 2- If the i-th <subtring-specifyer> of P is an <arbitrary-char- */
/* specifier>, the i-th subtring of M is any single <CR>. */
/* */
/* 3- If the i-th <subtring-specifyer> of P is an <arbitrary-string- */
/* specifier>, then the i-th subtring of M is any sequence of zero */
/* or more <CR>. */
/* */
/* 4- If the i-th <subtring-specifyer> of P is neither an <arbitrary- */
/* character-specifier> nor an <arbitrary-string-specifier>, then */
/* the i-th substring of M is equal to that <substring-specifier> */
/* according to the collating sequence of the <like-predicate>, */
/* without the appending of <space-character>, and has the same */
/* length as that <substring-specifier>. */
/* */
/* 5- The number of substrings of M is equal to the number of */
/* <subtring-specifiers> of P. */
/* */
/* Otherwise M like P is false. */
/***********************************************************************/
bool EvalLikePattern(LPCSTR sp, LPCSTR tp)
{
LPSTR p;
char c;
ssize_t n;
bool b, t = false;
if (trace(2))
htrc("Eval Like: sp=%s tp=%s\n",
(sp) ? sp : "Null", (tp) ? tp : "Null");
/********************************************************************/
/* If pattern is void, Like is true only if string is also void. */
/********************************************************************/
if (!*tp)
return (!*sp);
/********************************************************************/
/* Analyse eventual arbitrary specifications ahead of pattern. */
/********************************************************************/
for (p = (LPSTR)tp; p;)
switch (*p) { /* it can contain % and/or _ */
case '%': /* An % has been found */
t = true; /* Note eventual character skip */
p++;
break;
case '_': /* An _ has been found */
if (*sp) { /* If more character in string */
sp++; /* skip it */
p++;
} else
return false; /* Like condition is not met */
break;
default:
tp = p; /* Point to rest of template */
p = NULL; /* To stop For loop */
break;
} /* endswitch */
if ((p = (LPSTR)strpbrk(tp, "%_"))) /* Get position of next % or _ */
n = p - tp;
else
n = strlen(tp); /* Get length of pattern head */
if (trace(2))
htrc(" testing: t=%d sp=%s tp=%s p=%p\n", t, sp, tp, p);
if (n > (signed)strlen(sp)) /* If head is longer than strg */
b = false; /* Like condition is not met */
else if (n == 0) /* If void <substring-specifier> */
b = (t || !*sp); /* true if % or void strg. */
else if (!t) {
/*******************************************************************/
/* No character to skip, check occurrence of <subtring-specifier> */
/* at the very beginning of remaining string. */
/*******************************************************************/
if (p) {
if ((b = !strncmp(sp, tp, n)))
b = EvalLikePattern(sp + n, p);
} else
b = !strcmp(sp, tp); /* strg and tmp heads match */
} else
if (p)
/*****************************************************************/
/* Here is the case explaining why we need a recursive routine. */
/* The test must be done not only against the first occurrence */
/* of the <substring-specifier> in the remaining string, */
/* but also with all eventual succeeding ones. */
/*****************************************************************/
for (b = false, c = *p; !b && (signed)strlen(sp) >= n; sp++) {
*p = '\0'; /* Separate pattern header */
if ((sp = strstr(sp, tp))) {
*p = c;
b = EvalLikePattern(sp + n, p);
} else {
*p = c;
b = false;
break;
} /* endif s */
} /* endfor b, sp */
else {
sp += (strlen(sp) - n);
b = !strcmp(sp, tp);
} /* endif p */
if (trace(2))
htrc(" done: b=%d n=%d sp=%s tp=%s\n",
b, n, (sp) ? sp : "Null", tp);
return (b);
} /* end of EvalLikePattern */
/***********************************************************************/
/* MakeEscape: Escape some characters in a string. */
/***********************************************************************/
char *MakeEscape(PGLOBAL g, char* str, char q)
{
char *bufp;
int i, k, n = 0, len = (int)strlen(str);
for (i = 0; i < len; i++)
if (str[i] == q || str[i] == '\\')
n++;
if (!n)
return str;
else
bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1);
for (i = k = 0; i < len; i++) {
if (str[i] == q || str[i] == '\\')
bufp[k++] = '\\';
bufp[k++] = str[i];
} // endfor i
bufp[k] = 0;
return bufp;
} /* end of MakeEscape */
/***********************************************************************/
/* PlugConvertConstant: convert a Plug constant to an Xobject. */
/***********************************************************************/
void PlugConvertConstant(PGLOBAL g, void* & value, short& type)
{
if (trace(1))
htrc("PlugConvertConstant: value=%p type=%hd\n", value, type);
if (type != TYPE_XOBJECT) {
value = new(g) CONSTANT(g, value, type);
type = TYPE_XOBJECT;
} // endif type
} // end of PlugConvertConstant
/***********************************************************************/
/* Call the Flex preparser to convert a date format to a sscanf input */
/* format and a Strftime output format. Flag if not 0 indicates that */
/* non quoted blanks are not included in the output format. */
/***********************************************************************/
PDTP MakeDateFormat(PGLOBAL g, PCSZ dfmt, bool in, bool out, int flag)
{
int rc;
PDTP pdp = (PDTP)PlugSubAlloc(g, NULL, sizeof(DATPAR));
if (trace(1))
htrc("MakeDateFormat: dfmt=%s\n", dfmt);
memset(pdp, 0, sizeof(DATPAR));
pdp->Format = pdp->Curp = PlugDup(g, dfmt);
pdp->Outsize = 2 * strlen(dfmt) + 1;
if (in)
pdp->InFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
if (out)
pdp->OutFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
pdp->Flag = flag;
/*********************************************************************/
/* Call the FLEX generated parser. In multi-threading mode the next */
/* instruction is protected by mutex fmdflex using static variables. */
/*********************************************************************/
pthread_mutex_lock(&parmut);
rc = fmdflex(pdp);
pthread_mutex_unlock(&parmut);
if (trace(1))
htrc("Done: in=%s out=%s rc=%d\n", SVP(pdp->InFmt), SVP(pdp->OutFmt), rc);
return pdp;
} // end of MakeDateFormat
/***********************************************************************/
/* Extract the date from a formatted string according to format. */
/***********************************************************************/
int ExtractDate(char *dts, PDTP pdp, int defy, int val[6])
{
PCSZ fmt;
char c, d, e, W[8][12];
int i, k, m, numval;
int n, y = 30;
bool b = true; // true for null dates
if (pdp)
fmt = pdp->InFmt;
else // assume standard MySQL date format
fmt = "%4d-%2d-%2d %2d:%2d:%2d";
if (trace(2))
htrc("ExtractDate: dts=%s fmt=%s defy=%d\n", dts, fmt, defy);
// Set default values for time only use
if (defy) {
// This may be a default value for year
y = defy;
val[0] = y;
y = (y < 100) ? y : 30;
} else
val[0] = 70;
val[1] = 1;
val[2] = 1;
for (i = 3; i < 6; i++)
val[i] = 0;
numval = 0;
// Get the date field parse it with derived input format
m = sscanf(dts, fmt, W[0], W[1], W[2], W[3], W[4], W[5], W[6], W[7]);
if (m > pdp->Num)
m = pdp->Num;
for (i = 0; i < m; i++) {
if ((n = *(int*)W[i]))
b = false;
switch (k = pdp->Index[i]) {
case 0:
if (n < y)
n += 100;
val[0] = n;
numval = MY_MAX(numval, 1);
break;
case 1:
case 2:
case 3:
case 4:
case 5:
val[k] = n;
numval = MY_MAX(numval, k + 1);
break;
case -1:
c = toupper(W[i][0]);
d = toupper(W[i][1]);
e = toupper(W[i][2]);
switch (c) {
case 'J':
n = (d == 'A') ? 1
: (e == 'N') ? 6 : 7; break;
case 'F': n = 2; break;
case 'M':
n = (e == 'R') ? 3 : 5; break;
case 'A':
n = (d == 'P') ? 4 : 8; break;
break;
case 'S': n = 9; break;
case 'O': n = 10; break;
case 'N': n = 11; break;
case 'D': n = 12; break;
} /* endswitch c */
val[1] = n;
numval = MY_MAX(numval, 2);
break;
case -6:
c = toupper(W[i][0]);
n = val[3] % 12;
if (c == 'P')
n += 12;
val[3] = n;
break;
} // endswitch Plugpar
} // endfor i
if (trace(2))
htrc("numval=%d val=(%d,%d,%d,%d,%d,%d)\n",
numval, val[0], val[1], val[2], val[3], val[4], val[5]);
return (b) ? 0 : numval;
} // end of ExtractDate
/***********************************************************************/
/* Open file routine: the purpose of this routine is to make a list */
/* of all open file so they can be closed in SQLINIT on error jump. */
/***********************************************************************/
FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
{
FILE *fop;
PFBLOCK fp;
PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
if (trace(1)) {
htrc("PlugOpenFile: fname=%s ftype=%s\n", fname, ftype);
htrc("dbuserp=%p\n", dbuserp);
} // endif trace
if ((fop= global_fopen(g, MSGID_OPEN_MODE_STRERROR, fname, ftype)) != NULL) {
if (trace(1))
htrc(" fop=%p\n", fop);
fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
if (trace(1))
htrc(" fp=%p\n", fp);
// fname may be in volatile memory such as stack
fp->Fname = PlugDup(g, fname);
fp->Count = 1;
fp->Type = TYPE_FB_FILE;
fp->File = fop;
fp->Mode = MODE_ANY; // ???
fp->Next = dbuserp->Openlist;
dbuserp->Openlist = fp;
} /* endif fop */
if (trace(1))
htrc(" returning fop=%p\n", fop);
return (fop);
} // end of PlugOpenFile
/***********************************************************************/
/* Close file routine: the purpose of this routine is to avoid */
/* double closing that freeze the system on some Unix platforms. */
/***********************************************************************/
FILE *PlugReopenFile(PGLOBAL g, PFBLOCK fp, LPCSTR md)
{
FILE *fop;
if ((fop = global_fopen(g, MSGID_OPEN_MODE_STRERROR, fp->Fname, md))) {
fp->Count = 1;
fp->Type = TYPE_FB_FILE;
fp->File = fop;
} /* endif fop */
return (fop);
} // end of PlugOpenFile
/***********************************************************************/
/* Close file routine: the purpose of this routine is to avoid */
/* double closing that freeze the system on some Unix platforms. */
/***********************************************************************/
int PlugCloseFile(PGLOBAL g, PFBLOCK fp, bool all)
{
int rc = 0;
if (trace(1))
htrc("PlugCloseFile: fp=%p count=%hd type=%hd\n",
fp, ((fp) ? fp->Count : 0), ((fp) ? fp->Type : 0));
if (!fp || !fp->Count)
return rc;
switch (fp->Type) {
case TYPE_FB_FILE:
if (fclose((FILE *)fp->File) == EOF)
rc = errno;
fp->File = NULL;
fp->Mode = MODE_ANY;
fp->Count = 0;
break;
case TYPE_FB_MAP:
if ((fp->Count = (all) ? 0 : fp->Count - 1))
break;
if (CloseMemMap(fp->Memory, fp->Length))
rc = (int)GetLastError();
fp->Memory = NULL;
fp->Mode = MODE_ANY;
// fall through
case TYPE_FB_HANDLE:
if (fp->Handle && fp->Handle != INVALID_HANDLE_VALUE)
if (CloseFileHandle(fp->Handle))
rc = (rc) ? rc : (int)GetLastError();
fp->Handle = INVALID_HANDLE_VALUE;
fp->Mode = MODE_ANY;
fp->Count = 0;
break;
#ifdef DOMDOC_SUPPORT
case TYPE_FB_XML:
CloseXMLFile(g, fp, all);
break;
#endif // DOMDOC_SUPPORT
#ifdef LIBXML2_SUPPORT
case TYPE_FB_XML2:
CloseXML2File(g, fp, all);
break;
#endif // LIBXML2_SUPPORT
#ifdef ODBC_SUPPORT
case TYPE_FB_ODBC:
OdbcClose(g, fp);
fp->Count = 0;
fp->File = NULL;
break;
#endif // ODBC_SUPPORT
#ifdef ZIP_SUPPORT
case TYPE_FB_ZIP:
if (fp->Mode == MODE_INSERT)
((ZIPUTIL*)fp->File)->close();
else
((UNZIPUTL*)fp->File)->close();
fp->Memory = NULL;
fp->Mode = MODE_ANY;
fp->Count = 0;
fp->File = NULL;
break;
#endif // ZIP_SUPPORT
#ifdef JAVA_SUPPORT
case TYPE_FB_JAVA:
((JAVAConn*)fp->File)->Close();
fp->Count = 0;
fp->File = NULL;
break;
#endif // JAVA_SUPPORT
#ifdef CMGO_SUPPORT
case TYPE_FB_MONGO:
((CMgoConn*)fp->File)->Close();
fp->Count = 0;
fp->File = NULL;
break;
#endif // JAVA_SUPPORT
default:
rc = RC_FX;
} // endswitch Type
return rc;
} // end of PlugCloseFile
/***********************************************************************/
/* PlugCleanup: Cleanup remaining items of a SQL query. */
/***********************************************************************/
void PlugCleanup(PGLOBAL g, bool dofree)
{
PCATLG cat;
PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
// The test on Catalog is to avoid a Windows bug that can make
// LoadString in PlugGetMessage to fail in some case
if (!dbuserp || !(cat = dbuserp->Catalog))
return;
/*********************************************************************/
/* Close eventually still open/mapped files. */
/*********************************************************************/
for (PFBLOCK fp = dbuserp->Openlist; fp; fp = fp->Next)
PlugCloseFile(g, fp, true);
dbuserp->Openlist = NULL;
if (dofree) {
/*******************************************************************/
/* Cleanup any non suballocated memory still not freed. */
/*******************************************************************/
for (PMBLOCK mp = dbuserp->Memlist; mp; mp = mp->Next)
PlgDBfree(*mp);
dbuserp->Memlist = NULL;
/*******************************************************************/
/* If not using permanent storage catalog, reset volatile values. */
/*******************************************************************/
cat->Reset();
/*******************************************************************/
/* This is the place to reset the pointer on domains. */
/*******************************************************************/
dbuserp->Subcor = false;
dbuserp->Step = "New query"; // was STEP(PARSING_QUERY);
dbuserp->ProgMax = dbuserp->ProgCur = dbuserp->ProgSav = 0;
} // endif dofree
} // end of PlugCleanup
#if 0
/***********************************************************************/
/* That stupid Windows 98 does not provide this function. */
/***********************************************************************/
bool WritePrivateProfileInt(LPCSTR sec, LPCSTR key, int n, LPCSTR ini)
{
char buf[12];
sprintf(buf, "%d", n);
return WritePrivateProfileString(sec, key, buf, ini);
} // end of WritePrivateProfileInt
/***********************************************************************/
/* Retrieve a size from an INI file with eventual K or M following. */
/***********************************************************************/
int GetIniSize(char *section, char *key, char *def, char *ini)
{
char c, buff[32];
int i;
int n = 0;
GetPrivateProfileString(section, key, def, buff, sizeof(buff), ini);
if ((i = sscanf(buff, " %d %c ", &n, &c)) == 2)
switch (toupper(c)) {
case 'M':
n *= 1024;
case 'K':
n *= 1024;
} // endswitch c
if (trace(1))
htrc("GetIniSize: key=%s buff=%s i=%d n=%d\n", key, buff, i, n);
return n;
} // end of GetIniSize
/***********************************************************************/
/* Allocate a string retrieved from an INI file and return its address */
/***********************************************************************/
DllExport PSZ GetIniString(PGLOBAL g, void *mp, LPCSTR sec, LPCSTR key,
LPCSTR def, LPCSTR ini)
{
char buff[_MAX_PATH];
PSZ p;
int n, m = sizeof(buff);
char *buf = buff;
#if defined(_DEBUG)
assert (sec && key);
#endif
again:
n = GetPrivateProfileString(sec, key, def, buf, m, ini);
if (n == m - 1) {
// String may have been truncated, make sure to have all
if (buf != buff)
delete [] buf;
m *= 2;
buf = new char[m];
goto again;
} // endif n
p = (PSZ)PlugSubAlloc(g, mp, n + 1);
if (trace(1))
htrc("GetIniString: sec=%s key=%s buf=%s\n", sec, key, buf);
strcpy(p, buf);
if (buf != buff)
delete [] buf;
return p;
} // end of GetIniString
#endif // 0
/***********************************************************************/
/* GetAmName: return the name correponding to an AM code. */
/***********************************************************************/
char *GetAmName(PGLOBAL g, AMT am, void *memp)
{
char *amn= (char*)PlugSubAlloc(g, memp, 16);
switch (am) {
case TYPE_AM_ERROR: strcpy(amn, "ERROR"); break;
case TYPE_AM_ROWID: strcpy(amn, "ROWID"); break;
case TYPE_AM_FILID: strcpy(amn, "FILID"); break;
case TYPE_AM_VIEW: strcpy(amn, "VIEW"); break;
case TYPE_AM_COUNT: strcpy(amn, "COUNT"); break;
case TYPE_AM_DCD: strcpy(amn, "DCD"); break;
case TYPE_AM_CMS: strcpy(amn, "CMS"); break;
case TYPE_AM_MAP: strcpy(amn, "MAP"); break;
case TYPE_AM_FMT: strcpy(amn, "FMT"); break;
case TYPE_AM_CSV: strcpy(amn, "CSV"); break;
case TYPE_AM_MCV: strcpy(amn, "MCV"); break;
case TYPE_AM_DOS: strcpy(amn, "DOS"); break;
case TYPE_AM_FIX: strcpy(amn, "FIX"); break;
case TYPE_AM_BIN: strcpy(amn, "BIN"); break;
case TYPE_AM_VCT: strcpy(amn, "VEC"); break;
case TYPE_AM_VMP: strcpy(amn, "VMP"); break;
case TYPE_AM_DBF: strcpy(amn, "DBF"); break;
case TYPE_AM_QRY: strcpy(amn, "QRY"); break;
case TYPE_AM_SQL: strcpy(amn, "SQL"); break;
case TYPE_AM_PLG: strcpy(amn, "PLG"); break;
case TYPE_AM_PLM: strcpy(amn, "PLM"); break;
case TYPE_AM_DOM: strcpy(amn, "DOM"); break;
case TYPE_AM_DIR: strcpy(amn, "DIR"); break;
case TYPE_AM_ODBC: strcpy(amn, "ODBC"); break;
case TYPE_AM_JDBC: strcpy(amn, "JDBC"); break;
case TYPE_AM_MAC: strcpy(amn, "MAC"); break;
case TYPE_AM_OEM: strcpy(amn, "OEM"); break;
case TYPE_AM_OUT: strcpy(amn, "OUT"); break;
default: sprintf(amn, "OEM(%d)", am);
} // endswitch am
return amn;
} // end of GetAmName
#if defined(SE_CATCH)
/***********************************************************************/
/* GetExceptionDesc: return the description of an exception code. */
/***********************************************************************/
char *GetExceptionDesc(PGLOBAL g, unsigned int e)
{
char *p;
switch (e) {
case EXCEPTION_GUARD_PAGE:
p = MSG(GUARD_PAGE);
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
p = MSG(DATA_MISALIGN);
break;
case EXCEPTION_BREAKPOINT:
p = MSG(BREAKPOINT);
break;
case EXCEPTION_SINGLE_STEP:
p = MSG(SINGLE_STEP);
break;
case EXCEPTION_ACCESS_VIOLATION:
p = MSG(ACCESS_VIOLATN);
break;
case EXCEPTION_IN_PAGE_ERROR:
p = MSG(PAGE_ERROR);
break;
case EXCEPTION_INVALID_HANDLE:
p = MSG(INVALID_HANDLE);
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
p = MSG(ILLEGAL_INSTR);
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
p = MSG(NONCONT_EXCEPT);
break;
case EXCEPTION_INVALID_DISPOSITION:
p = MSG(INVALID_DISP);
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
p = MSG(ARRAY_BNDS_EXCD);
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
p = MSG(FLT_DENORMAL_OP);
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
p = MSG(FLT_ZERO_DIVIDE);
break;
case EXCEPTION_FLT_INEXACT_RESULT:
p = MSG(FLT_BAD_RESULT);
break;
case EXCEPTION_FLT_INVALID_OPERATION:
p = MSG(FLT_INVALID_OP);
break;
case EXCEPTION_FLT_OVERFLOW:
p = MSG(FLT_OVERFLOW);
break;
case EXCEPTION_FLT_STACK_CHECK:
p = MSG(FLT_STACK_CHECK);
break;
case EXCEPTION_FLT_UNDERFLOW:
p = MSG(FLT_UNDERFLOW);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
p = MSG(INT_ZERO_DIVIDE);
break;
case EXCEPTION_INT_OVERFLOW:
p = MSG(INT_OVERFLOW);
break;
case EXCEPTION_PRIV_INSTRUCTION:
p = MSG(PRIV_INSTR);
break;
case EXCEPTION_STACK_OVERFLOW:
p = MSG(STACK_OVERFLOW);
break;
case CONTROL_C_EXIT:
p = MSG(CONTROL_C_EXIT);
break;
case STATUS_NO_MEMORY:
p = MSG(NO_MEMORY);
break;
default:
p = MSG(UNKNOWN_EXCPT);
break;
} // endswitch nSE
return p;
} // end of GetExceptionDesc
#endif // SE_CATCH
/***********************************************************************/
/* PlgDBalloc: allocates or suballocates memory conditionally. */
/* If mp.Sub is true at entry, this forces suballocation. */
/* If the memory is allocated, makes an entry in an allocation list */
/* so it can be freed at the normal or error query completion. */
/***********************************************************************/
void *PlgDBalloc(PGLOBAL g, void *area, MBLOCK& mp)
{
//bool b;
size_t maxsub, minsub;
void *arp = (area) ? area : g->Sarea;
PPOOLHEADER pph = (PPOOLHEADER)arp;
if (mp.Memp) {
// This is a reallocation. If this block is not suballocated, it
// was already placed in the chain of memory blocks and we must
// not do it again as it can trigger a loop when freeing them.
// Note: this works if blocks can be reallocated only once.
// Otherwise a new boolean must be added to the block that
// indicate that it is chained, or a test on the whole chain be
// done to check whether the block is already there.
// b = mp.Sub;
mp.Sub = false; // Restrict suballocation to one quarter
} // endif Memp
// Suballoc when possible if mp.Sub is initially true, but leaving
// a minimum amount of storage for future operations such as the
// optimize recalculation after insert; otherwise
// suballoc only if size is smaller than one quarter of free mem.
minsub = (pph->FreeBlk + pph->To_Free + 524248) >> 2;
maxsub = (pph->FreeBlk < minsub) ? 0 : pph->FreeBlk - minsub;
mp.Sub = mp.Size <= ((mp.Sub) ? maxsub : (maxsub >> 2));
if (trace(2))
htrc("PlgDBalloc: in %p size=%zd used=%zd free=%zd sub=%d\n",
arp, mp.Size, pph->To_Free, pph->FreeBlk, mp.Sub);
if (!mp.Sub) {
// For allocations greater than one fourth of remaining storage
// in the area, do allocate from virtual storage.
const char*v = "malloc";
#if defined(_WIN32)
if (mp.Size >= BIGMEM) {
v = "VirtualAlloc";
mp.Memp = VirtualAlloc(NULL, mp.Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
} else
#endif
mp.Memp = malloc(mp.Size);
if (trace(8))
htrc("PlgDBalloc: %s(%zd) at %p\n", v, mp.Size, mp.Memp);
if (!mp.Inlist && mp.Memp) {
// New allocated block, put it in the memory block chain.
PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
mp.Next = dbuserp->Memlist;
dbuserp->Memlist = &mp;
mp.Inlist = true;
} // endif mp
} else
// Suballocating is Ok.
mp.Memp = PlugSubAlloc(g, area, mp.Size);
return mp.Memp;
} // end of PlgDBalloc
/***********************************************************************/
/* PlgDBrealloc: reallocates memory conditionally. */
/* Note that this routine can fail only when block size is increased */
/* because otherwise we keep the old storage on failure. */
/***********************************************************************/
void *PlgDBrealloc(PGLOBAL g, void *area, MBLOCK& mp, size_t newsize)
{
MBLOCK m;
#if defined(_DEBUG)
// assert (mp.Memp != NULL);
#endif
if (trace(2))
htrc("PlgDBrealloc: %p size=%zd sub=%d\n", mp.Memp, mp.Size, mp.Sub);
if (newsize == mp.Size)
return mp.Memp; // Nothing to do
else
m = mp;
if (!mp.Sub && mp.Size < BIGMEM && newsize < BIGMEM) {
// Allocation was done by malloc, try to use realloc but
// suballoc if newsize is smaller than one quarter of free mem.
size_t maxsub;
PPOOLHEADER pph = (PPOOLHEADER)((area) ? area : g->Sarea);
maxsub = (pph->FreeBlk < 131072) ? 0 : pph->FreeBlk - 131072;
if ((mp.Sub = (newsize <= (maxsub >> 2)))) {
mp.Memp = PlugSubAlloc(g, area, newsize);
memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
PlgDBfree(m); // Free the old block
} else {
if (!(mp.Memp = realloc(mp.Memp, newsize))) {
mp = m; // Possible only if newsize > Size
return NULL; // Failed
} else if (trace(8))
htrc("PlgDBrealloc: realloc(%ld) at %p\n", newsize, mp.Memp);
} // endif's
mp.Size = newsize;
} else if (!mp.Sub || newsize > mp.Size) {
// Was suballocated or Allocation was done by VirtualAlloc
// Make a new allocation and copy the useful part
// Note: DO NOT reset Memp and Sub so we know that this
// is a reallocation in PlgDBalloc
mp.Size = newsize;
if (PlgDBalloc(g, area, mp)) {
memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
PlgDBfree(m); // Free the old block
} else {
mp = m; // No space to realloc, do nothing
if (newsize > m.Size)
return NULL; // Failed
} // endif PlgDBalloc
} // endif's
if (trace(8))
htrc(" newsize=%zd newp=%p sub=%d\n", mp.Size, mp.Memp, mp.Sub);
return mp.Memp;
} // end of PlgDBrealloc
/***********************************************************************/
/* PlgDBfree: free memory if not suballocated. */
/***********************************************************************/
void PlgDBfree(MBLOCK& mp)
{
if (!mp.Sub && mp.Memp) {
const char*v = "free";
#if defined(_WIN32)
if (mp.Size >= BIGMEM) {
v = "VirtualFree";
VirtualFree(mp.Memp, 0, MEM_RELEASE);
} else
#endif
free(mp.Memp);
if (trace(8))
htrc("PlgDBfree: %s(%p) size=%d\n", v, mp.Memp, mp.Size);
} // endif mp
// Do not reset Next to avoid cutting the Mblock chain
mp.Memp = NULL;
mp.Sub = false;
mp.Size = 0;
} // end of PlgDBfree
/***********************************************************************/
/* Program for sub-allocating one item in a storage area. */
/* Note: This function is equivalent to PlugSubAlloc except that in */
/* case of insufficient memory, it returns NULL instead of doing a */
/* throw. The caller must test the return value for error. */
/***********************************************************************/
void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size)
{
PPOOLHEADER pph; // Points on area header.
if (!memp)
/*******************************************************************/
/* Allocation is to be done in the Sarea. */
/*******************************************************************/
memp = g->Sarea;
//size = ((size + 3) / 4) * 4; /* Round up size to multiple of 4 */
size = ((size + 7) / 8) * 8; /* Round up size to multiple of 8 */
pph = (PPOOLHEADER)memp;
if (trace(16))
htrc("PlgDBSubAlloc: memp=%p size=%zd used=%zd free=%zd\n",
memp, size, pph->To_Free, pph->FreeBlk);
if (size > pph->FreeBlk) { /* Not enough memory left in pool */
snprintf(g->Message, sizeof(g->Message),
"Not enough memory in Work area for request of %zd (used=%zd free=%zd)",
size, pph->To_Free, pph->FreeBlk);
if (trace(1))
htrc("%s\n", g->Message);
return NULL;
} // endif size
/*********************************************************************/
/* Do the suballocation the simplest way. */
/*********************************************************************/
memp = MakePtr(memp, pph->To_Free); // Points to suballocated block
pph->To_Free += size; // New offset of pool free block
pph->FreeBlk -= size; // New size of pool free block
if (trace(16))
htrc("Done memp=%p used=%zd free=%zd\n",
memp, pph->To_Free, pph->FreeBlk);
return (memp);
} // end of PlgDBSubAlloc
/***********************************************************************/
/* Program for sub-allocating and copying a string in a storage area. */
/***********************************************************************/
char *PlgDBDup(PGLOBAL g, const char *str)
{
if (str) {
char *sm = (char*)PlgDBSubAlloc(g, NULL, strlen(str) + 1);
if (sm)
strcpy(sm, str);
return sm;
} else
return NULL;
} // end of PlgDBDup
/***********************************************************************/
/* PUTOUT: Plug DB object typing routine. */
/***********************************************************************/
void PlugPutOut(PGLOBAL g, FILE *f, short t, void *v, uint n)
{
char m[64];
if (trace(1))
htrc("PUTOUT: f=%p t=%d v=%p n=%d\n", f, t, v, n);
if (!v)
return;
memset(m, ' ', n); /* Make margin string */
m[n] = '\0';
n += 2; /* Increase margin */
switch (t) {
case TYPE_ERROR:
fprintf(f, "--> %s\n", (PSZ)v);
break;
case TYPE_STRING:
case TYPE_PSZ:
fprintf(f, "%s%s\n", m, (PSZ)v);
break;
case TYPE_DOUBLE:
fprintf(f, "%s%lf\n", m, *(double *)v);
break;
case TYPE_LIST:
case TYPE_COLIST:
case TYPE_COL:
{PPARM p;
if (t == TYPE_LIST)
fprintf(f, "%s%s\n", m, MSG(LIST));
else
fprintf(f, "%s%s\n", m, "Colist:");
for (p = (PPARM)v; p; p = p->Next)
PlugPutOut(g, f, p->Type, p->Value, n);
} break;
case TYPE_INT:
fprintf(f, "%s%d\n", m, *(int *)v);
break;
case TYPE_SHORT:
fprintf(f, "%s%hd\n", m, *(short *)v);
break;
case TYPE_TINY:
fprintf(f, "%s%d\n", m, (int)*(char *)v);
break;
case TYPE_VOID:
break;
case TYPE_SQL:
case TYPE_TABLE:
case TYPE_TDB:
case TYPE_XOBJECT:
((PBLOCK)v)->Printf(g, f, n-2);
break;
default:
fprintf(f, "%s%s %d\n", m, MSG(ANSWER_TYPE), t);
} /* endswitch */
return;
} /* end of PlugPutOut */
/***********************************************************************/
/* NewPointer: makes a table of pointer values to be changed later. */
/***********************************************************************/
DllExport void NewPointer(PTABS t, void *oldv, void *newv)
{
PTABPTR tp;
if (!oldv) /* error ?????????? */
return;
if (!t->P1 || t->P1->Num == 50)
{
if (!(tp = new TABPTR)) {
PGLOBAL g = t->G;
snprintf(g->Message, sizeof(g->Message), "NewPointer: %s", MSG(MEM_ALLOC_ERROR));
throw 3;
} else {
tp->Next = t->P1;
tp->Num = 0;
t->P1 = tp;
} /* endif tp */
}
t->P1->Old[t->P1->Num] = oldv;
t->P1->New[t->P1->Num++] = newv;
} /* end of NewPointer */
#if 0
/***********************************************************************/
/* Compare two files and return 0 if they are identical, else 1. */
/***********************************************************************/
int FileComp(PGLOBAL g, char *file1, char *file2)
{
char *fn[2], *bp[2], buff1[4096], buff2[4096];
int i, k, n[2], h[2] = {-1,-1};
int len[2], rc = -1;
fn[0] = file1; fn[1] = file2;
bp[0] = buff1; bp[1] = buff2;
for (i = 0; i < 2; i++) {
#if defined(_WIN32)
h[i]= global_open(g, MSGID_NONE, fn[i], _O_RDONLY | _O_BINARY);
#else // !_WIN32
h[i]= global_open(g, MSGOD_NONE, fn[i], O_RDONLY);
#endif // !_WIN32
if (h[i] == -1) {
// if (errno != ENOENT) {
snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
"rb", (int)errno, fn[i]);
strcat(strcat(g->Message, ": "), strerror(errno));
throw 666;
// } else
// len[i] = 0; // File does not exist yet
} else {
if ((len[i] = _filelength(h[i])) < 0) {
snprintf(g->Message, sizeof(g->Message), MSG(FILELEN_ERROR), "_filelength", fn[i]);
throw 666;
} // endif len
} // endif h
} // endfor i
if (len[0] != len[1])
rc = 1;
while (rc == -1) {
for (i = 0; i < 2; i++)
if ((n[i] = read(h[i], bp[i], 4096)) < 0) {
snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), fn[i], strerror(errno));
goto fin;
} // endif n
if (n[0] != n[1])
rc = 1;
else if (*n == 0)
rc = 0;
else for (k = 0; k < *n; k++)
if (*(bp[0] + k) != *(bp[1] + k)) {
rc = 1;
goto fin;
} // endif bp
} // endwhile
fin:
for (i = 0; i < 2; i++)
if (h[i] != -1)
close(h[i]);
return rc;
} // end of FileComp
#endif // 0