1/********** PlgDBUtl Fpe C++ Program Source Code File (.CPP) ***********/
2/* PROGRAM NAME: PLGDBUTL */
3/* ------------- */
4/* Version 4.1 */
5/* */
6/* COPYRIGHT: */
7/* ---------- */
8/* (C) Copyright to the author Olivier BERTRAND 1998-2018 */
9/* */
10/* WHAT THIS PROGRAM DOES: */
11/* ----------------------- */
12/* Utility functions used by DB semantic routines. */
13/* */
14/* WHAT YOU NEED TO COMPILE THIS PROGRAM: */
15/* -------------------------------------- */
16/* */
17/* REQUIRED FILES: */
18/* --------------- */
19/* See Readme.C for a list and description of required SYSTEM files. */
20/* */
21/* PLGDBUTL.C - Source code */
22/* GLOBAL.H - Global declaration file */
23/* PLGDBSEM.H - DB application declaration file */
24/* */
25/* REQUIRED LIBRARIES: */
26/* ------------------- */
27/* OS2.LIB - OS2 libray */
28/* LLIBCE.LIB - Protect mode/standard combined large model C */
29/* library */
30/* */
31/* REQUIRED PROGRAMS: */
32/* ------------------ */
33/* IBM, MS, Borland or GNU C++ Compiler */
34/* IBM, MS, Borland or GNU Linker */
35/***********************************************************************/
36
37/***********************************************************************/
38/* Include relevant MariaDB header file. */
39/***********************************************************************/
40#include "my_global.h"
41#include "my_pthread.h"
42#if defined(__WIN__)
43#include <io.h>
44#include <fcntl.h>
45#include <errno.h>
46#define BIGMEM 1048576 // 1 Megabyte
47#else // !__WIN__
48#include <unistd.h>
49#include <fcntl.h>
50//#if defined(THREAD)
51#include <pthread.h>
52//#endif // THREAD
53#include <stdarg.h>
54#define BIGMEM 2147483647 // Max int value
55#endif // !__WIN__
56#include <locale.h>
57
58/***********************************************************************/
59/* Include application header files */
60/***********************************************************************/
61#include "global.h" // header containing all global declarations.
62#include "plgdbsem.h" // header containing the DB applic. declarations.
63#include "preparse.h" // For DATPAR
64#include "osutil.h"
65#include "maputil.h"
66#include "catalog.h"
67#include "colblk.h"
68#include "xtable.h" // header of TBX, TDB and TDBASE classes
69#include "tabcol.h" // header of XTAB and COLUMN classes
70#include "valblk.h"
71#include "rcmsg.h"
72#ifdef ZIP_SUPPORT
73#include "filamzip.h"
74#endif // ZIP_SUPPORT
75#ifdef JAVA_SUPPORT
76#include "javaconn.h"
77#endif // JAVA_SUPPORT
78#ifdef CMGO_SUPPORT
79#include "cmgoconn.h"
80#endif // JAVA_SUPPORT
81
82/***********************************************************************/
83/* DB static variables. */
84/***********************************************************************/
85bool Initdone = false;
86bool plugin = false; // True when called by the XDB plugin handler
87
88extern "C" {
89extern char version[];
90} // extern "C"
91
92//#if defined(__WIN__)
93//extern CRITICAL_SECTION parsec; // Used calling the Flex parser
94//#else // !__WIN__
95extern pthread_mutex_t parmut;
96//#endif // !__WIN__
97
98// The debug trace used by the main thread
99FILE *pfile = NULL;
100
101MBLOCK Nmblk = {NULL, false, 0, false, NULL}; // Used to init MBLOCK's
102
103/***********************************************************************/
104/* Routines called externally and internally by utility routines. */
105/***********************************************************************/
106bool PlugEvalLike(PGLOBAL, LPCSTR, LPCSTR, bool);
107bool EvalLikePattern(LPCSTR, LPCSTR);
108void PlugConvertConstant(PGLOBAL, void* &, short&);
109
110#ifdef DOMDOC_SUPPORT
111void CloseXMLFile(PGLOBAL, PFBLOCK, bool);
112#endif // DOMDOC_SUPPORT
113
114#ifdef LIBXML2_SUPPORT
115#include "libdoc.h"
116#endif // LIBXML2_SUPPORT
117
118#ifdef ODBC_SUPPORT
119void OdbcClose(PGLOBAL g, PFBLOCK fp);
120#endif // ODBC_SUPPORT
121
122/***********************************************************************/
123/* Routines for file IO with error reporting to g->Message */
124/* Note: errno and strerror must be called before the message file */
125/* is read in the case of XMSG compile. */
126/***********************************************************************/
127static void global_open_error_msg(GLOBAL *g, int msgid, const char *path,
128 const char *mode)
129{
130 int len, rno= (int)errno;
131 char errmsg[256]= "";
132
133 strncat(errmsg, strerror(errno), 255);
134
135 switch (msgid)
136 {
137 case MSGID_CANNOT_OPEN:
138 len= snprintf(g->Message, sizeof(g->Message) - 1,
139 MSG(CANNOT_OPEN), // Cannot open %s
140 path);
141 break;
142
143 case MSGID_OPEN_MODE_ERROR:
144 len= snprintf(g->Message, sizeof(g->Message) - 1,
145 MSG(OPEN_MODE_ERROR), // "Open(%s) error %d on %s"
146 mode, rno, path);
147 break;
148
149 case MSGID_OPEN_MODE_STRERROR:
150 {char fmt[256];
151 strcat(strcpy(fmt, MSG(OPEN_MODE_ERROR)), ": %s");
152 len= snprintf(g->Message, sizeof(g->Message) - 1,
153 fmt, // Open(%s) error %d on %s: %s
154 mode, rno, path, errmsg);
155 }break;
156
157 case MSGID_OPEN_STRERROR:
158 len= snprintf(g->Message, sizeof(g->Message) - 1,
159 MSG(OPEN_STRERROR), // "open error: %s"
160 errmsg);
161 break;
162
163 case MSGID_OPEN_ERROR_AND_STRERROR:
164 len= snprintf(g->Message, sizeof(g->Message) - 1,
165 //OPEN_ERROR does not work, as it wants mode %d (not %s)
166 //MSG(OPEN_ERROR) "%s",// "Open error %d in mode %d on %s: %s"
167 "Open error %d in mode %s on %s: %s",
168 rno, mode, path, errmsg);
169 break;
170
171 case MSGID_OPEN_EMPTY_FILE:
172 len= snprintf(g->Message, sizeof(g->Message) - 1,
173 MSG(OPEN_EMPTY_FILE), // "Opening empty file %s: %s"
174 path, errmsg);
175 break;
176
177 default:
178 DBUG_ASSERT(0);
179 /* Fall through*/
180 case 0:
181 len= 0;
182 }
183 g->Message[len]= '\0';
184}
185
186
187FILE *global_fopen(GLOBAL *g, int msgid, const char *path, const char *mode)
188{
189 FILE *f;
190 if (!(f= fopen(path, mode)))
191 global_open_error_msg(g, msgid, path, mode);
192 return f;
193}
194
195
196int global_open(GLOBAL *g, int msgid, const char *path, int flags)
197{
198 int h;
199 if ((h= open(path, flags)) <= 0)
200 global_open_error_msg(g, msgid, path, "");
201 return h;
202}
203
204
205int global_open(GLOBAL *g, int msgid, const char *path, int flags, int mode)
206{
207 int h;
208 if ((h= open(path, flags, mode)) <= 0)
209 {
210 char modestr[64];
211 snprintf(modestr, sizeof(modestr), "%d", mode);
212 global_open_error_msg(g, msgid, path, modestr);
213 }
214 return h;
215}
216
217DllExport void SetTrc(void)
218{
219 // If tracing is on, debug must be initialized.
220 debug = pfile;
221} // end of SetTrc
222
223/**************************************************************************/
224/* SubAllocate the result structure that will contain result data. */
225/**************************************************************************/
226PQRYRES PlgAllocResult(PGLOBAL g, int ncol, int maxres, int ids,
227 int *buftyp, XFLD *fldtyp,
228 unsigned int *length, bool blank, bool nonull)
229{
230 char cname[NAM_LEN+1];
231 int i;
232 PCOLRES *pcrp, crp;
233 PQRYRES qrp;
234
235 try {
236 /**********************************************************************/
237 /* Allocate the structure used to contain the result set. */
238 /**********************************************************************/
239 qrp = (PQRYRES)PlugSubAlloc(g, NULL, sizeof(QRYRES));
240 pcrp = &qrp->Colresp;
241 qrp->Continued = false;
242 qrp->Truncated = false;
243 qrp->Info = false;
244 qrp->Suball = true;
245 qrp->Maxres = maxres;
246 qrp->Maxsize = 0;
247 qrp->Nblin = 0;
248 qrp->Nbcol = 0; // will be ncol
249 qrp->Cursor = 0;
250 qrp->BadLines = 0;
251
252 for (i = 0; i < ncol; i++) {
253 *pcrp = (PCOLRES)PlugSubAlloc(g, NULL, sizeof(COLRES));
254 crp = *pcrp;
255 pcrp = &crp->Next;
256 memset(crp, 0, sizeof(COLRES));
257 crp->Colp = NULL;
258 crp->Ncol = ++qrp->Nbcol;
259 crp->Type = buftyp[i];
260 crp->Length = length[i];
261 crp->Clen = GetTypeSize(crp->Type, length[i]);
262 crp->Prec = 0;
263
264 if (ids > 0) {
265#if defined(XMSG)
266 // Get header from message file
267 strncpy(cname, PlugReadMessage(g, ids + crp->Ncol, NULL), NAM_LEN);
268 cname[NAM_LEN] = 0; // for truncated long names
269#else // !XMSG
270 GetRcString(ids + crp->Ncol, cname, sizeof(cname));
271#endif // !XMSG
272 crp->Name = (PSZ)PlugDup(g, cname);
273 } else
274 crp->Name = NULL; // Will be set by caller
275
276 if (fldtyp)
277 crp->Fld = fldtyp[i];
278 else
279 crp->Fld = FLD_NO;
280
281 // Allocate the Value Block that will contain data
282 if (crp->Length || nonull)
283 crp->Kdata = AllocValBlock(g, NULL, crp->Type, maxres,
284 crp->Length, 0, true, blank, false);
285 else
286 crp->Kdata = NULL;
287
288 if (trace(1))
289 htrc("Column(%d) %s type=%d len=%d value=%p\n",
290 crp->Ncol, crp->Name, crp->Type, crp->Length, crp->Kdata);
291
292 } // endfor i
293
294 *pcrp = NULL;
295
296 } catch (int n) {
297 htrc("Exception %d: %s\n", n, g->Message);
298 qrp = NULL;
299 } catch (const char *msg) {
300 strcpy(g->Message, msg);
301 htrc("%s\n", g->Message);
302 qrp = NULL;
303 } // end catch
304
305 return qrp;
306} // end of PlgAllocResult
307
308/***********************************************************************/
309/* Allocate and initialize the new DB User Block. */
310/***********************************************************************/
311PDBUSER PlgMakeUser(PGLOBAL g)
312 {
313 PDBUSER dbuserp;
314
315 if (!(dbuserp = (PDBUSER)malloc(sizeof(DBUSERBLK)))) {
316 sprintf(g->Message, MSG(MALLOC_ERROR), "PlgMakeUser");
317 return NULL;
318 } // endif dbuserp
319
320 memset(dbuserp, 0, sizeof(DBUSERBLK));
321 dbuserp->Maxbmp = MAXBMP;
322//dbuserp->UseTemp = TMP_AUTO;
323 dbuserp->Check = CHK_ALL;
324 strcpy(dbuserp->Server, "CONNECT");
325 return dbuserp;
326 } // end of PlgMakeUser
327
328/***********************************************************************/
329/* PlgGetUser: returns DBUSER block pointer. */
330/***********************************************************************/
331PDBUSER PlgGetUser(PGLOBAL g)
332 {
333 PDBUSER dup = (PDBUSER)((g->Activityp) ? g->Activityp->Aptr : NULL);
334
335 if (!dup)
336 strcpy(g->Message, MSG(APPL_NOT_INIT));
337
338 return dup;
339 } // end of PlgGetUser
340
341/***********************************************************************/
342/* PlgGetCatalog: returns CATALOG class pointer. */
343/***********************************************************************/
344PCATLG PlgGetCatalog(PGLOBAL g, bool jump)
345 {
346 PDBUSER dbuserp = PlgGetUser(g);
347 PCATLG cat = (dbuserp) ? dbuserp->Catalog : NULL;
348
349 if (!cat && jump) {
350 // Raise exception so caller doesn't have to check return value
351 strcpy(g->Message, MSG(NO_ACTIVE_DB));
352 throw 1;
353 } // endif cat
354
355 return cat;
356 } // end of PlgGetCatalog
357
358#if 0
359/***********************************************************************/
360/* PlgGetDataPath: returns the default data path. */
361/***********************************************************************/
362char *PlgGetDataPath(PGLOBAL g)
363 {
364 PCATLG cat = PlgGetCatalog(g, false);
365
366 return (cat) ? cat->GetDataPath() : NULL;
367 } // end of PlgGetDataPath
368#endif // 0
369
370/***********************************************************************/
371/* This function returns a database path. */
372/***********************************************************************/
373char *SetPath(PGLOBAL g, const char *path)
374{
375 char *buf= NULL;
376
377 if (path) {
378 size_t len = strlen(path) + (*path != '.' ? 4 : 1);
379
380 if (!(buf = (char*)PlgDBSubAlloc(g, NULL, len)))
381 return NULL;
382
383 if (PlugIsAbsolutePath(path)) {
384 strcpy(buf, path);
385 return buf;
386 } // endif path
387
388 if (*path != '.') {
389#if defined(__WIN__)
390 const char *s = "\\";
391#else // !__WIN__
392 const char *s = "/";
393#endif // !__WIN__
394 strcat(strcat(strcat(strcpy(buf, "."), s), path), s);
395 } else
396 strcpy(buf, path);
397
398 } // endif path
399
400 return buf;
401} // end of SetPath
402
403/***********************************************************************/
404/* Extract from a path name the required component. */
405/* This function assumes there is enough space in the buffer. */
406/***********************************************************************/
407char *ExtractFromPath(PGLOBAL g, char *pBuff, char *FileName, OPVAL op)
408 {
409 char *drive = NULL, *direc = NULL, *fname = NULL, *ftype = NULL;
410
411 switch (op) { // Determine which part to extract
412#if defined(__WIN__)
413 case OP_FDISK: drive = pBuff; break;
414#endif // !UNIX
415 case OP_FPATH: direc = pBuff; break;
416 case OP_FNAME: fname = pBuff; break;
417 case OP_FTYPE: ftype = pBuff; break;
418 default:
419 sprintf(g->Message, MSG(INVALID_OPER), op, "ExtractFromPath");
420 return NULL;
421 } // endswitch op
422
423 // Now do the extraction
424 _splitpath(FileName, drive, direc, fname, ftype);
425 return pBuff;
426 } // end of PlgExtractFromPath
427
428/***********************************************************************/
429/* Check the occurence and matching of a pattern against a string. */
430/* Because this function is only used for catalog name checking, */
431/* it must be case insensitive. */
432/***********************************************************************/
433static bool PlugCheckPattern(PGLOBAL g, LPCSTR string, LPCSTR pat)
434 {
435 if (pat && strlen(pat)) {
436 // This leaves 2048 bytes (MAX_STR / 2) for each components
437 LPSTR name = g->Message + MAX_STR / 2;
438
439 strlwr(strcpy(name, string));
440 strlwr(strcpy(g->Message, pat)); // Can be modified by Eval
441 return EvalLikePattern(name, g->Message);
442 } else
443 return true;
444
445 } // end of PlugCheckPattern
446
447/***********************************************************************/
448/* PlugEvalLike: evaluates a LIKE clause. */
449/* Syntaxe: M like P escape C. strg->M, pat->P, C not implemented yet */
450/***********************************************************************/
451bool PlugEvalLike(PGLOBAL g, LPCSTR strg, LPCSTR pat, bool ci)
452 {
453 char *tp, *sp;
454 bool b;
455
456 if (trace(2))
457 htrc("LIKE: strg='%s' pattern='%s'\n", strg, pat);
458
459 if (ci) { /* Case insensitive test */
460 if (strlen(pat) + strlen(strg) + 1 < MAX_STR)
461 tp = g->Message;
462 else if (!(tp = new char[strlen(pat) + strlen(strg) + 2])) {
463 strcpy(g->Message, MSG(NEW_RETURN_NULL));
464 throw (int)OP_LIKE;
465 } /* endif tp */
466
467 sp = tp + strlen(pat) + 1;
468 strlwr(strcpy(tp, pat)); /* Make a lower case copy of pat */
469 strlwr(strcpy(sp, strg)); /* Make a lower case copy of strg */
470 } else { /* Case sensitive test */
471 if (strlen(pat) < MAX_STR) /* In most of the case for small pat */
472 tp = g->Message; /* Use this as temporary work space. */
473 else if (!(tp = new char[strlen(pat) + 1])) {
474 strcpy(g->Message, MSG(NEW_RETURN_NULL));
475 throw (int)OP_LIKE;
476 } /* endif tp */
477
478 strcpy(tp, pat); /* Make a copy to be worked into */
479 sp = (char*)strg;
480 } /* endif ci */
481
482 b = EvalLikePattern(sp, tp);
483
484 if (tp != g->Message) /* If working space was obtained */
485 delete [] tp; /* by the use of new, delete it. */
486
487 return (b);
488 } /* end of PlugEvalLike */
489
490/***********************************************************************/
491/* M and P are variable length character string. If M and P are zero */
492/* length strings then the Like predicate is true. */
493/* */
494/* The Like predicate is true if: */
495/* */
496/* 1- A subtring of M is a sequence of 0 or more contiguous <CR> of M */
497/* and each <CR> of M is part of exactly one substring. */
498/* */
499/* 2- If the i-th <subtring-specifyer> of P is an <arbitrary-char- */
500/* specifier>, the i-th subtring of M is any single <CR>. */
501/* */
502/* 3- If the i-th <subtring-specifyer> of P is an <arbitrary-string- */
503/* specifier>, then the i-th subtring of M is any sequence of zero */
504/* or more <CR>. */
505/* */
506/* 4- If the i-th <subtring-specifyer> of P is neither an <arbitrary- */
507/* character-specifier> nor an <arbitrary-string-specifier>, then */
508/* the i-th substring of M is equal to that <substring-specifier> */
509/* according to the collating sequence of the <like-predicate>, */
510/* without the appending of <space-character>, and has the same */
511/* length as that <substring-specifier>. */
512/* */
513/* 5- The number of substrings of M is equal to the number of */
514/* <subtring-specifiers> of P. */
515/* */
516/* Otherwise M like P is false. */
517/***********************************************************************/
518bool EvalLikePattern(LPCSTR sp, LPCSTR tp)
519 {
520 LPSTR p;
521 char c;
522 ssize_t n;
523 bool b, t = false;
524
525 if (trace(2))
526 htrc("Eval Like: sp=%s tp=%s\n",
527 (sp) ? sp : "Null", (tp) ? tp : "Null");
528
529 /********************************************************************/
530 /* If pattern is void, Like is true only if string is also void. */
531 /********************************************************************/
532 if (!*tp)
533 return (!*sp);
534
535 /********************************************************************/
536 /* Analyse eventual arbitrary specifications ahead of pattern. */
537 /********************************************************************/
538 for (p = (LPSTR)tp; p;)
539 switch (*p) { /* it can contain % and/or _ */
540 case '%': /* An % has been found */
541 t = true; /* Note eventual character skip */
542 p++;
543 break;
544 case '_': /* An _ has been found */
545 if (*sp) { /* If more character in string */
546 sp++; /* skip it */
547 p++;
548 } else
549 return false; /* Like condition is not met */
550
551 break;
552 default:
553 tp = p; /* Point to rest of template */
554 p = NULL; /* To stop For loop */
555 break;
556 } /* endswitch */
557
558 if ((p = (LPSTR)strpbrk(tp, "%_"))) /* Get position of next % or _ */
559 n = p - tp;
560 else
561 n = strlen(tp); /* Get length of pattern head */
562
563 if (trace(2))
564 htrc(" testing: t=%d sp=%s tp=%s p=%p\n", t, sp, tp, p);
565
566 if (n > (signed)strlen(sp)) /* If head is longer than strg */
567 b = false; /* Like condition is not met */
568 else if (n == 0) /* If void <substring-specifier> */
569 b = (t || !*sp); /* true if % or void strg. */
570 else if (!t) {
571 /*******************************************************************/
572 /* No character to skip, check occurence of <subtring-specifier> */
573 /* at the very beginning of remaining string. */
574 /*******************************************************************/
575 if (p) {
576 if ((b = !strncmp(sp, tp, n)))
577 b = EvalLikePattern(sp + n, p);
578
579 } else
580 b = !strcmp(sp, tp); /* strg and tmp heads match */
581
582 } else
583 if (p)
584 /*****************************************************************/
585 /* Here is the case explaining why we need a recursive routine. */
586 /* The test must be done not only against the first occurence */
587 /* of the <substring-specifier> in the remaining string, */
588 /* but also with all eventual succeeding ones. */
589 /*****************************************************************/
590 for (b = false, c = *p; !b && (signed)strlen(sp) >= n; sp++) {
591 *p = '\0'; /* Separate pattern header */
592
593 if ((sp = strstr(sp, tp))) {
594 *p = c;
595 b = EvalLikePattern(sp + n, p);
596 } else {
597 *p = c;
598 b = false;
599 break;
600 } /* endif s */
601
602 } /* endfor b, sp */
603
604 else {
605 sp += (strlen(sp) - n);
606 b = !strcmp(sp, tp);
607 } /* endif p */
608
609 if (trace(2))
610 htrc(" done: b=%d n=%d sp=%s tp=%s\n",
611 b, n, (sp) ? sp : "Null", tp);
612
613 return (b);
614 } /* end of EvalLikePattern */
615
616/***********************************************************************/
617/* MakeEscape: Escape some characters in a string. */
618/***********************************************************************/
619char *MakeEscape(PGLOBAL g, char* str, char q)
620 {
621 char *bufp;
622 int i, k, n = 0, len = (int)strlen(str);
623
624 for (i = 0; i < len; i++)
625 if (str[i] == q || str[i] == '\\')
626 n++;
627
628 if (!n)
629 return str;
630 else
631 bufp = (char*)PlugSubAlloc(g, NULL, len + n + 1);
632
633 for (i = k = 0; i < len; i++) {
634 if (str[i] == q || str[i] == '\\')
635 bufp[k++] = '\\';
636
637 bufp[k++] = str[i];
638 } // endfor i
639
640 bufp[k] = 0;
641 return bufp;
642 } /* end of MakeEscape */
643
644/***********************************************************************/
645/* PlugConvertConstant: convert a Plug constant to an Xobject. */
646/***********************************************************************/
647void PlugConvertConstant(PGLOBAL g, void* & value, short& type)
648 {
649 if (trace(1))
650 htrc("PlugConvertConstant: value=%p type=%hd\n", value, type);
651
652 if (type != TYPE_XOBJECT) {
653 value = new(g) CONSTANT(g, value, type);
654 type = TYPE_XOBJECT;
655 } // endif type
656
657 } // end of PlugConvertConstant
658
659/***********************************************************************/
660/* Call the Flex preparser to convert a date format to a sscanf input */
661/* format and a Strftime output format. Flag if not 0 indicates that */
662/* non quoted blanks are not included in the output format. */
663/***********************************************************************/
664PDTP MakeDateFormat(PGLOBAL g, PCSZ dfmt, bool in, bool out, int flag)
665{
666 int rc;
667 PDTP pdp = (PDTP)PlugSubAlloc(g, NULL, sizeof(DATPAR));
668
669 if (trace(1))
670 htrc("MakeDateFormat: dfmt=%s\n", dfmt);
671
672 memset(pdp, 0, sizeof(DATPAR));
673 pdp->Format = pdp->Curp = PlugDup(g, dfmt);
674 pdp->Outsize = 2 * strlen(dfmt) + 1;
675
676 if (in)
677 pdp->InFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
678
679 if (out)
680 pdp->OutFmt = (char*)PlugSubAlloc(g, NULL, pdp->Outsize);
681
682 pdp->Flag = flag;
683
684 /*********************************************************************/
685 /* Call the FLEX generated parser. In multi-threading mode the next */
686 /* instruction is protected by mutex fmdflex using static variables. */
687 /*********************************************************************/
688 pthread_mutex_lock(&parmut);
689 rc = fmdflex(pdp);
690 pthread_mutex_unlock(&parmut);
691
692 if (trace(1))
693 htrc("Done: in=%s out=%s rc=%d\n", SVP(pdp->InFmt), SVP(pdp->OutFmt), rc);
694
695 return pdp;
696} // end of MakeDateFormat
697
698/***********************************************************************/
699/* Extract the date from a formatted string according to format. */
700/***********************************************************************/
701int ExtractDate(char *dts, PDTP pdp, int defy, int val[6])
702 {
703 PCSZ fmt;
704 char c, d, e, W[8][12];
705 int i, k, m, numval;
706 int n, y = 30;
707 bool b = true; // true for null dates
708
709 if (pdp)
710 fmt = pdp->InFmt;
711 else // assume standard MySQL date format
712 fmt = "%4d-%2d-%2d %2d:%2d:%2d";
713
714 if (trace(2))
715 htrc("ExtractDate: dts=%s fmt=%s defy=%d\n", dts, fmt, defy);
716
717 // Set default values for time only use
718 if (defy) {
719 // This may be a default value for year
720 y = defy;
721 val[0] = y;
722 y = (y < 100) ? y : 30;
723 } else
724 val[0] = 70;
725
726 val[1] = 1;
727 val[2] = 1;
728
729 for (i = 3; i < 6; i++)
730 val[i] = 0;
731
732 numval = 0;
733
734 // Get the date field parse it with derived input format
735 m = sscanf(dts, fmt, W[0], W[1], W[2], W[3], W[4], W[5], W[6], W[7]);
736
737 if (m > pdp->Num)
738 m = pdp->Num;
739
740 for (i = 0; i < m; i++) {
741 if ((n = *(int*)W[i]))
742 b = false;
743
744 switch (k = pdp->Index[i]) {
745 case 0:
746 if (n < y)
747 n += 100;
748
749 val[0] = n;
750 numval = MY_MAX(numval, 1);
751 break;
752 case 1:
753 case 2:
754 case 3:
755 case 4:
756 case 5:
757 val[k] = n;
758 numval = MY_MAX(numval, k + 1);
759 break;
760 case -1:
761 c = toupper(W[i][0]);
762 d = toupper(W[i][1]);
763 e = toupper(W[i][2]);
764
765 switch (c) {
766 case 'J':
767 n = (d == 'A') ? 1
768 : (e == 'N') ? 6 : 7; break;
769 case 'F': n = 2; break;
770 case 'M':
771 n = (e == 'R') ? 3 : 5; break;
772 case 'A':
773 n = (d == 'P') ? 4 : 8; break;
774 break;
775 case 'S': n = 9; break;
776 case 'O': n = 10; break;
777 case 'N': n = 11; break;
778 case 'D': n = 12; break;
779 } /* endswitch c */
780
781 val[1] = n;
782 numval = MY_MAX(numval, 2);
783 break;
784 case -6:
785 c = toupper(W[i][0]);
786 n = val[3] % 12;
787
788 if (c == 'P')
789 n += 12;
790
791 val[3] = n;
792 break;
793 } // endswitch Plugpar
794
795 } // endfor i
796
797 if (trace(2))
798 htrc("numval=%d val=(%d,%d,%d,%d,%d,%d)\n",
799 numval, val[0], val[1], val[2], val[3], val[4], val[5]);
800
801 return (b) ? 0 : numval;
802 } // end of ExtractDate
803
804/***********************************************************************/
805/* Open file routine: the purpose of this routine is to make a list */
806/* of all open file so they can be closed in SQLINIT on error jump. */
807/***********************************************************************/
808FILE *PlugOpenFile(PGLOBAL g, LPCSTR fname, LPCSTR ftype)
809 {
810 FILE *fop;
811 PFBLOCK fp;
812 PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
813
814 if (trace(1)) {
815 htrc("PlugOpenFile: fname=%s ftype=%s\n", fname, ftype);
816 htrc("dbuserp=%p\n", dbuserp);
817 } // endif trace
818
819 if ((fop= global_fopen(g, MSGID_OPEN_MODE_STRERROR, fname, ftype)) != NULL) {
820 if (trace(1))
821 htrc(" fop=%p\n", fop);
822
823 fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
824
825 if (trace(1))
826 htrc(" fp=%p\n", fp);
827
828 // fname may be in volatile memory such as stack
829 fp->Fname = PlugDup(g, fname);
830 fp->Count = 1;
831 fp->Type = TYPE_FB_FILE;
832 fp->File = fop;
833 fp->Mode = MODE_ANY; // ???
834 fp->Next = dbuserp->Openlist;
835 dbuserp->Openlist = fp;
836 } /* endif fop */
837
838 if (trace(1))
839 htrc(" returning fop=%p\n", fop);
840
841 return (fop);
842 } // end of PlugOpenFile
843
844/***********************************************************************/
845/* Close file routine: the purpose of this routine is to avoid */
846/* double closing that freeze the system on some Unix platforms. */
847/***********************************************************************/
848FILE *PlugReopenFile(PGLOBAL g, PFBLOCK fp, LPCSTR md)
849 {
850 FILE *fop;
851
852 if ((fop = global_fopen(g, MSGID_OPEN_MODE_STRERROR, fp->Fname, md))) {
853 fp->Count = 1;
854 fp->Type = TYPE_FB_FILE;
855 fp->File = fop;
856 } /* endif fop */
857
858 return (fop);
859 } // end of PlugOpenFile
860
861/***********************************************************************/
862/* Close file routine: the purpose of this routine is to avoid */
863/* double closing that freeze the system on some Unix platforms. */
864/***********************************************************************/
865int PlugCloseFile(PGLOBAL g, PFBLOCK fp, bool all)
866 {
867 int rc = 0;
868
869 if (trace(1))
870 htrc("PlugCloseFile: fp=%p count=%hd type=%hd\n",
871 fp, ((fp) ? fp->Count : 0), ((fp) ? fp->Type : 0));
872
873 if (!fp || !fp->Count)
874 return rc;
875
876 switch (fp->Type) {
877 case TYPE_FB_FILE:
878 if (fclose((FILE *)fp->File) == EOF)
879 rc = errno;
880
881 fp->File = NULL;
882 fp->Mode = MODE_ANY;
883 fp->Count = 0;
884 break;
885 case TYPE_FB_MAP:
886 if ((fp->Count = (all) ? 0 : fp->Count - 1))
887 break;
888
889 if (CloseMemMap(fp->Memory, fp->Length))
890 rc = (int)GetLastError();
891
892 fp->Memory = NULL;
893 fp->Mode = MODE_ANY;
894 // fall through
895 case TYPE_FB_HANDLE:
896 if (fp->Handle && fp->Handle != INVALID_HANDLE_VALUE)
897 if (CloseFileHandle(fp->Handle))
898 rc = (rc) ? rc : (int)GetLastError();
899
900 fp->Handle = INVALID_HANDLE_VALUE;
901 fp->Mode = MODE_ANY;
902 fp->Count = 0;
903 break;
904#ifdef DOMDOC_SUPPORT
905 case TYPE_FB_XML:
906 CloseXMLFile(g, fp, all);
907 break;
908#endif // DOMDOC_SUPPORT
909#ifdef LIBXML2_SUPPORT
910 case TYPE_FB_XML2:
911 CloseXML2File(g, fp, all);
912 break;
913#endif // LIBXML2_SUPPORT
914#ifdef ODBC_SUPPORT
915 case TYPE_FB_ODBC:
916 OdbcClose(g, fp);
917 fp->Count = 0;
918 fp->File = NULL;
919 break;
920#endif // ODBC_SUPPORT
921#ifdef ZIP_SUPPORT
922 case TYPE_FB_ZIP:
923 if (fp->Mode == MODE_INSERT)
924 ((ZIPUTIL*)fp->File)->close();
925 else
926 ((UNZIPUTL*)fp->File)->close();
927
928 fp->Memory = NULL;
929 fp->Mode = MODE_ANY;
930 fp->Count = 0;
931 fp->File = NULL;
932 break;
933#endif // ZIP_SUPPORT
934#ifdef JAVA_SUPPORT
935 case TYPE_FB_JAVA:
936 ((JAVAConn*)fp->File)->Close();
937 fp->Count = 0;
938 fp->File = NULL;
939 break;
940#endif // JAVA_SUPPORT
941#ifdef CMGO_SUPPORT
942 case TYPE_FB_MONGO:
943 ((CMgoConn*)fp->File)->Close();
944 fp->Count = 0;
945 fp->File = NULL;
946 break;
947#endif // JAVA_SUPPORT
948 default:
949 rc = RC_FX;
950 } // endswitch Type
951
952 return rc;
953 } // end of PlugCloseFile
954
955/***********************************************************************/
956/* PlugCleanup: Cleanup remaining items of a SQL query. */
957/***********************************************************************/
958void PlugCleanup(PGLOBAL g, bool dofree)
959 {
960 PCATLG cat;
961 PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
962
963 // The test on Catalog is to avoid a Windows bug that can make
964 // LoadString in PlugGetMessage to fail in some case
965 if (!dbuserp || !(cat = dbuserp->Catalog))
966 return;
967
968 /*********************************************************************/
969 /* Close eventually still open/mapped files. */
970 /*********************************************************************/
971 for (PFBLOCK fp = dbuserp->Openlist; fp; fp = fp->Next)
972 PlugCloseFile(g, fp, true);
973
974 dbuserp->Openlist = NULL;
975
976 if (dofree) {
977 /*******************************************************************/
978 /* Cleanup any non suballocated memory still not freed. */
979 /*******************************************************************/
980 for (PMBLOCK mp = dbuserp->Memlist; mp; mp = mp->Next)
981 PlgDBfree(*mp);
982
983 dbuserp->Memlist = NULL;
984
985 /*******************************************************************/
986 /* If not using permanent storage catalog, reset volatile values. */
987 /*******************************************************************/
988 cat->Reset();
989
990 /*******************************************************************/
991 /* This is the place to reset the pointer on domains. */
992 /*******************************************************************/
993 dbuserp->Subcor = false;
994 dbuserp->Step = "New query"; // was STEP(PARSING_QUERY);
995 dbuserp->ProgMax = dbuserp->ProgCur = dbuserp->ProgSav = 0;
996 } // endif dofree
997
998 } // end of PlugCleanup
999
1000#if 0
1001/***********************************************************************/
1002/* That stupid Windows 98 does not provide this function. */
1003/***********************************************************************/
1004bool WritePrivateProfileInt(LPCSTR sec, LPCSTR key, int n, LPCSTR ini)
1005 {
1006 char buf[12];
1007
1008 sprintf(buf, "%d", n);
1009 return WritePrivateProfileString(sec, key, buf, ini);
1010 } // end of WritePrivateProfileInt
1011
1012/***********************************************************************/
1013/* Retrieve a size from an INI file with eventual K or M following. */
1014/***********************************************************************/
1015int GetIniSize(char *section, char *key, char *def, char *ini)
1016 {
1017 char c, buff[32];
1018 int i;
1019 int n = 0;
1020
1021 GetPrivateProfileString(section, key, def, buff, sizeof(buff), ini);
1022
1023 if ((i = sscanf(buff, " %d %c ", &n, &c)) == 2)
1024 switch (toupper(c)) {
1025 case 'M':
1026 n *= 1024;
1027 case 'K':
1028 n *= 1024;
1029 } // endswitch c
1030
1031 if (trace(1))
1032 htrc("GetIniSize: key=%s buff=%s i=%d n=%d\n", key, buff, i, n);
1033
1034 return n;
1035 } // end of GetIniSize
1036
1037/***********************************************************************/
1038/* Allocate a string retrieved from an INI file and return its address */
1039/***********************************************************************/
1040DllExport PSZ GetIniString(PGLOBAL g, void *mp, LPCSTR sec, LPCSTR key,
1041 LPCSTR def, LPCSTR ini)
1042 {
1043 char buff[_MAX_PATH];
1044 PSZ p;
1045 int n, m = sizeof(buff);
1046 char *buf = buff;
1047
1048#if defined(_DEBUG)
1049 assert (sec && key);
1050#endif
1051
1052 again:
1053 n = GetPrivateProfileString(sec, key, def, buf, m, ini);
1054
1055 if (n == m - 1) {
1056 // String may have been truncated, make sure to have all
1057 if (buf != buff)
1058 delete [] buf;
1059
1060 m *= 2;
1061 buf = new char[m];
1062 goto again;
1063 } // endif n
1064
1065 p = (PSZ)PlugSubAlloc(g, mp, n + 1);
1066
1067 if (trace(1))
1068 htrc("GetIniString: sec=%s key=%s buf=%s\n", sec, key, buf);
1069
1070 strcpy(p, buf);
1071
1072 if (buf != buff)
1073 delete [] buf;
1074
1075 return p;
1076 } // end of GetIniString
1077#endif // 0
1078
1079/***********************************************************************/
1080/* GetAmName: return the name correponding to an AM code. */
1081/***********************************************************************/
1082char *GetAmName(PGLOBAL g, AMT am, void *memp)
1083 {
1084 char *amn= (char*)PlugSubAlloc(g, memp, 16);
1085
1086 switch (am) {
1087 case TYPE_AM_ERROR: strcpy(amn, "ERROR"); break;
1088 case TYPE_AM_ROWID: strcpy(amn, "ROWID"); break;
1089 case TYPE_AM_FILID: strcpy(amn, "FILID"); break;
1090 case TYPE_AM_VIEW: strcpy(amn, "VIEW"); break;
1091 case TYPE_AM_COUNT: strcpy(amn, "COUNT"); break;
1092 case TYPE_AM_DCD: strcpy(amn, "DCD"); break;
1093 case TYPE_AM_CMS: strcpy(amn, "CMS"); break;
1094 case TYPE_AM_MAP: strcpy(amn, "MAP"); break;
1095 case TYPE_AM_FMT: strcpy(amn, "FMT"); break;
1096 case TYPE_AM_CSV: strcpy(amn, "CSV"); break;
1097 case TYPE_AM_MCV: strcpy(amn, "MCV"); break;
1098 case TYPE_AM_DOS: strcpy(amn, "DOS"); break;
1099 case TYPE_AM_FIX: strcpy(amn, "FIX"); break;
1100 case TYPE_AM_BIN: strcpy(amn, "BIN"); break;
1101 case TYPE_AM_VCT: strcpy(amn, "VEC"); break;
1102 case TYPE_AM_VMP: strcpy(amn, "VMP"); break;
1103 case TYPE_AM_DBF: strcpy(amn, "DBF"); break;
1104 case TYPE_AM_QRY: strcpy(amn, "QRY"); break;
1105 case TYPE_AM_SQL: strcpy(amn, "SQL"); break;
1106 case TYPE_AM_PLG: strcpy(amn, "PLG"); break;
1107 case TYPE_AM_PLM: strcpy(amn, "PLM"); break;
1108 case TYPE_AM_DOM: strcpy(amn, "DOM"); break;
1109 case TYPE_AM_DIR: strcpy(amn, "DIR"); break;
1110 case TYPE_AM_ODBC: strcpy(amn, "ODBC"); break;
1111 case TYPE_AM_JDBC: strcpy(amn, "JDBC"); break;
1112 case TYPE_AM_MAC: strcpy(amn, "MAC"); break;
1113 case TYPE_AM_OEM: strcpy(amn, "OEM"); break;
1114 case TYPE_AM_OUT: strcpy(amn, "OUT"); break;
1115 default: sprintf(amn, "OEM(%d)", am);
1116 } // endswitch am
1117
1118 return amn;
1119 } // end of GetAmName
1120
1121#if defined(SE_CATCH)
1122/***********************************************************************/
1123/* GetExceptionDesc: return the description of an exception code. */
1124/***********************************************************************/
1125char *GetExceptionDesc(PGLOBAL g, unsigned int e)
1126 {
1127 char *p;
1128
1129 switch (e) {
1130 case EXCEPTION_GUARD_PAGE:
1131 p = MSG(GUARD_PAGE);
1132 break;
1133 case EXCEPTION_DATATYPE_MISALIGNMENT:
1134 p = MSG(DATA_MISALIGN);
1135 break;
1136 case EXCEPTION_BREAKPOINT:
1137 p = MSG(BREAKPOINT);
1138 break;
1139 case EXCEPTION_SINGLE_STEP:
1140 p = MSG(SINGLE_STEP);
1141 break;
1142 case EXCEPTION_ACCESS_VIOLATION:
1143 p = MSG(ACCESS_VIOLATN);
1144 break;
1145 case EXCEPTION_IN_PAGE_ERROR:
1146 p = MSG(PAGE_ERROR);
1147 break;
1148 case EXCEPTION_INVALID_HANDLE:
1149 p = MSG(INVALID_HANDLE);
1150 break;
1151 case EXCEPTION_ILLEGAL_INSTRUCTION:
1152 p = MSG(ILLEGAL_INSTR);
1153 break;
1154 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
1155 p = MSG(NONCONT_EXCEPT);
1156 break;
1157 case EXCEPTION_INVALID_DISPOSITION:
1158 p = MSG(INVALID_DISP);
1159 break;
1160 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
1161 p = MSG(ARRAY_BNDS_EXCD);
1162 break;
1163 case EXCEPTION_FLT_DENORMAL_OPERAND:
1164 p = MSG(FLT_DENORMAL_OP);
1165 break;
1166 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
1167 p = MSG(FLT_ZERO_DIVIDE);
1168 break;
1169 case EXCEPTION_FLT_INEXACT_RESULT:
1170 p = MSG(FLT_BAD_RESULT);
1171 break;
1172 case EXCEPTION_FLT_INVALID_OPERATION:
1173 p = MSG(FLT_INVALID_OP);
1174 break;
1175 case EXCEPTION_FLT_OVERFLOW:
1176 p = MSG(FLT_OVERFLOW);
1177 break;
1178 case EXCEPTION_FLT_STACK_CHECK:
1179 p = MSG(FLT_STACK_CHECK);
1180 break;
1181 case EXCEPTION_FLT_UNDERFLOW:
1182 p = MSG(FLT_UNDERFLOW);
1183 break;
1184 case EXCEPTION_INT_DIVIDE_BY_ZERO:
1185 p = MSG(INT_ZERO_DIVIDE);
1186 break;
1187 case EXCEPTION_INT_OVERFLOW:
1188 p = MSG(INT_OVERFLOW);
1189 break;
1190 case EXCEPTION_PRIV_INSTRUCTION:
1191 p = MSG(PRIV_INSTR);
1192 break;
1193 case EXCEPTION_STACK_OVERFLOW:
1194 p = MSG(STACK_OVERFLOW);
1195 break;
1196 case CONTROL_C_EXIT:
1197 p = MSG(CONTROL_C_EXIT);
1198 break;
1199 case STATUS_NO_MEMORY:
1200 p = MSG(NO_MEMORY);
1201 break;
1202 default:
1203 p = MSG(UNKNOWN_EXCPT);
1204 break;
1205 } // endswitch nSE
1206
1207 return p;
1208 } // end of GetExceptionDesc
1209#endif // SE_CATCH
1210
1211/***********************************************************************/
1212/* PlgDBalloc: allocates or suballocates memory conditionally. */
1213/* If mp.Sub is true at entry, this forces suballocation. */
1214/* If the memory is allocated, makes an entry in an allocation list */
1215/* so it can be freed at the normal or error query completion. */
1216/***********************************************************************/
1217void *PlgDBalloc(PGLOBAL g, void *area, MBLOCK& mp)
1218{
1219//bool b;
1220 size_t maxsub, minsub;
1221 void *arp = (area) ? area : g->Sarea;
1222 PPOOLHEADER pph = (PPOOLHEADER)arp;
1223
1224 if (mp.Memp) {
1225 // This is a reallocation. If this block is not suballocated, it
1226 // was already placed in the chain of memory blocks and we must
1227 // not do it again as it can trigger a loop when freeing them.
1228 // Note: this works if blocks can be reallocated only once.
1229 // Otherwise a new boolean must be added to the block that
1230 // indicate that it is chained, or a test on the whole chain be
1231 // done to check whether the block is already there.
1232// b = mp.Sub;
1233 mp.Sub = false; // Restrict suballocation to one quarter
1234 } // endif Memp
1235
1236 // Suballoc when possible if mp.Sub is initially true, but leaving
1237 // a minimum amount of storage for future operations such as the
1238 // optimize recalculation after insert; otherwise
1239 // suballoc only if size is smaller than one quarter of free mem.
1240 minsub = (pph->FreeBlk + pph->To_Free + 524248) >> 2;
1241 maxsub = (pph->FreeBlk < minsub) ? 0 : pph->FreeBlk - minsub;
1242 mp.Sub = mp.Size <= ((mp.Sub) ? maxsub : (maxsub >> 2));
1243
1244 if (trace(2))
1245 htrc("PlgDBalloc: in %p size=%d used=%d free=%d sub=%d\n",
1246 arp, mp.Size, pph->To_Free, pph->FreeBlk, mp.Sub);
1247
1248 if (!mp.Sub) {
1249 // For allocations greater than one fourth of remaining storage
1250 // in the area, do allocate from virtual storage.
1251 const char*v = "malloc";
1252#if defined(__WIN__)
1253 if (mp.Size >= BIGMEM) {
1254 v = "VirtualAlloc";
1255 mp.Memp = VirtualAlloc(NULL, mp.Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
1256 } else
1257#endif
1258 mp.Memp = malloc(mp.Size);
1259
1260 if (trace(8))
1261 htrc("PlgDBalloc: %s(%d) at %p\n", v, mp.Size, mp.Memp);
1262
1263 if (!mp.Inlist && mp.Memp) {
1264 // New allocated block, put it in the memory block chain.
1265 PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;
1266
1267 mp.Next = dbuserp->Memlist;
1268 dbuserp->Memlist = &mp;
1269 mp.Inlist = true;
1270 } // endif mp
1271
1272 } else
1273 // Suballocating is Ok.
1274 mp.Memp = PlugSubAlloc(g, area, mp.Size);
1275
1276 return mp.Memp;
1277} // end of PlgDBalloc
1278
1279/***********************************************************************/
1280/* PlgDBrealloc: reallocates memory conditionally. */
1281/* Note that this routine can fail only when block size is increased */
1282/* because otherwise we keep the old storage on failure. */
1283/***********************************************************************/
1284void *PlgDBrealloc(PGLOBAL g, void *area, MBLOCK& mp, size_t newsize)
1285 {
1286 MBLOCK m;
1287
1288#if defined(_DEBUG)
1289// assert (mp.Memp != NULL);
1290#endif
1291
1292 if (trace(2))
1293 htrc("PlgDBrealloc: %p size=%d sub=%d\n", mp.Memp, mp.Size, mp.Sub);
1294
1295 if (newsize == mp.Size)
1296 return mp.Memp; // Nothing to do
1297 else
1298 m = mp;
1299
1300 if (!mp.Sub && mp.Size < BIGMEM && newsize < BIGMEM) {
1301 // Allocation was done by malloc, try to use realloc but
1302 // suballoc if newsize is smaller than one quarter of free mem.
1303 size_t maxsub;
1304 PPOOLHEADER pph = (PPOOLHEADER)((area) ? area : g->Sarea);
1305
1306 maxsub = (pph->FreeBlk < 131072) ? 0 : pph->FreeBlk - 131072;
1307
1308 if ((mp.Sub = (newsize <= (maxsub >> 2)))) {
1309 mp.Memp = PlugSubAlloc(g, area, newsize);
1310 memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
1311 PlgDBfree(m); // Free the old block
1312 } else {
1313 if (!(mp.Memp = realloc(mp.Memp, newsize))) {
1314 mp = m; // Possible only if newsize > Size
1315 return NULL; // Failed
1316 } else if (trace(8))
1317 htrc("PlgDBrealloc: realloc(%ld) at %p\n", newsize, mp.Memp);
1318
1319 } // endif's
1320
1321 mp.Size = newsize;
1322 } else if (!mp.Sub || newsize > mp.Size) {
1323 // Was suballocated or Allocation was done by VirtualAlloc
1324 // Make a new allocation and copy the useful part
1325 // Note: DO NOT reset Memp and Sub so we know that this
1326 // is a reallocation in PlgDBalloc
1327 mp.Size = newsize;
1328
1329 if (PlgDBalloc(g, area, mp)) {
1330 memcpy(mp.Memp, m.Memp, MY_MIN(m.Size, newsize));
1331 PlgDBfree(m); // Free the old block
1332 } else {
1333 mp = m; // No space to realloc, do nothing
1334
1335 if (newsize > m.Size)
1336 return NULL; // Failed
1337
1338 } // endif PlgDBalloc
1339
1340 } // endif's
1341
1342 if (trace(8))
1343 htrc(" newsize=%d newp=%p sub=%d\n", mp.Size, mp.Memp, mp.Sub);
1344
1345 return mp.Memp;
1346 } // end of PlgDBrealloc
1347
1348/***********************************************************************/
1349/* PlgDBfree: free memory if not suballocated. */
1350/***********************************************************************/
1351void PlgDBfree(MBLOCK& mp)
1352 {
1353 if (!mp.Sub && mp.Memp) {
1354 const char*v = "free";
1355#if defined(__WIN__)
1356 if (mp.Size >= BIGMEM) {
1357 v = "VirtualFree";
1358 VirtualFree(mp.Memp, 0, MEM_RELEASE);
1359 } else
1360#endif
1361 free(mp.Memp);
1362
1363 if (trace(8))
1364 htrc("PlgDBfree: %s(%p) size=%d\n", v, mp.Memp, mp.Size);
1365
1366 } // endif mp
1367
1368 // Do not reset Next to avoid cutting the Mblock chain
1369 mp.Memp = NULL;
1370 mp.Sub = false;
1371 mp.Size = 0;
1372 } // end of PlgDBfree
1373
1374/***********************************************************************/
1375/* Program for sub-allocating one item in a storage area. */
1376/* Note: This function is equivalent to PlugSubAlloc except that in */
1377/* case of insufficient memory, it returns NULL instead of doing a */
1378/* throw. The caller must test the return value for error. */
1379/***********************************************************************/
1380void *PlgDBSubAlloc(PGLOBAL g, void *memp, size_t size)
1381 {
1382 PPOOLHEADER pph; // Points on area header.
1383
1384 if (!memp)
1385 /*******************************************************************/
1386 /* Allocation is to be done in the Sarea. */
1387 /*******************************************************************/
1388 memp = g->Sarea;
1389
1390//size = ((size + 3) / 4) * 4; /* Round up size to multiple of 4 */
1391 size = ((size + 7) / 8) * 8; /* Round up size to multiple of 8 */
1392 pph = (PPOOLHEADER)memp;
1393
1394 if (trace(16))
1395 htrc("PlgDBSubAlloc: memp=%p size=%d used=%d free=%d\n",
1396 memp, size, pph->To_Free, pph->FreeBlk);
1397
1398 if ((uint)size > pph->FreeBlk) { /* Not enough memory left in pool */
1399 sprintf(g->Message,
1400 "Not enough memory in Work area for request of %d (used=%d free=%d)",
1401 (int) size, pph->To_Free, pph->FreeBlk);
1402
1403 if (trace(1))
1404 htrc("%s\n", g->Message);
1405
1406 return NULL;
1407 } // endif size
1408
1409 /*********************************************************************/
1410 /* Do the suballocation the simplest way. */
1411 /*********************************************************************/
1412 memp = MakePtr(memp, pph->To_Free); // Points to suballocated block
1413 pph->To_Free += size; // New offset of pool free block
1414 pph->FreeBlk -= size; // New size of pool free block
1415
1416 if (trace(16))
1417 htrc("Done memp=%p used=%d free=%d\n",
1418 memp, pph->To_Free, pph->FreeBlk);
1419
1420 return (memp);
1421 } // end of PlgDBSubAlloc
1422
1423/***********************************************************************/
1424/* Program for sub-allocating and copying a string in a storage area. */
1425/***********************************************************************/
1426char *PlgDBDup(PGLOBAL g, const char *str)
1427 {
1428 if (str) {
1429 char *sm = (char*)PlgDBSubAlloc(g, NULL, strlen(str) + 1);
1430
1431 if (sm)
1432 strcpy(sm, str);
1433
1434 return sm;
1435 } else
1436 return NULL;
1437
1438 } // end of PlgDBDup
1439
1440/***********************************************************************/
1441/* PUTOUT: Plug DB object typing routine. */
1442/***********************************************************************/
1443void PlugPutOut(PGLOBAL g, FILE *f, short t, void *v, uint n)
1444 {
1445 char m[64];
1446
1447 if (trace(1))
1448 htrc("PUTOUT: f=%p t=%d v=%p n=%d\n", f, t, v, n);
1449
1450 if (!v)
1451 return;
1452
1453 memset(m, ' ', n); /* Make margin string */
1454 m[n] = '\0';
1455 n += 2; /* Increase margin */
1456
1457 switch (t) {
1458 case TYPE_ERROR:
1459 fprintf(f, "--> %s\n", (PSZ)v);
1460 break;
1461
1462 case TYPE_STRING:
1463 case TYPE_PSZ:
1464 fprintf(f, "%s%s\n", m, (PSZ)v);
1465 break;
1466
1467 case TYPE_DOUBLE:
1468 fprintf(f, "%s%lf\n", m, *(double *)v);
1469 break;
1470
1471 case TYPE_LIST:
1472 case TYPE_COLIST:
1473 case TYPE_COL:
1474 {PPARM p;
1475
1476 if (t == TYPE_LIST)
1477 fprintf(f, "%s%s\n", m, MSG(LIST));
1478 else
1479 fprintf(f, "%s%s\n", m, "Colist:");
1480
1481 for (p = (PPARM)v; p; p = p->Next)
1482 PlugPutOut(g, f, p->Type, p->Value, n);
1483
1484 } break;
1485
1486 case TYPE_INT:
1487 fprintf(f, "%s%d\n", m, *(int *)v);
1488 break;
1489
1490 case TYPE_SHORT:
1491 fprintf(f, "%s%hd\n", m, *(short *)v);
1492 break;
1493
1494 case TYPE_TINY:
1495 fprintf(f, "%s%d\n", m, (int)*(char *)v);
1496 break;
1497
1498 case TYPE_VOID:
1499 break;
1500
1501 case TYPE_SQL:
1502 case TYPE_TABLE:
1503 case TYPE_TDB:
1504 case TYPE_XOBJECT:
1505 ((PBLOCK)v)->Printf(g, f, n-2);
1506 break;
1507
1508 default:
1509 fprintf(f, "%s%s %d\n", m, MSG(ANSWER_TYPE), t);
1510 } /* endswitch */
1511
1512 return;
1513 } /* end of PlugPutOut */
1514
1515/***********************************************************************/
1516/* NewPointer: makes a table of pointer values to be changed later. */
1517/***********************************************************************/
1518DllExport void NewPointer(PTABS t, void *oldv, void *newv)
1519 {
1520 PTABPTR tp;
1521
1522 if (!oldv) /* error ?????????? */
1523 return;
1524
1525 if (!t->P1 || t->P1->Num == 50)
1526 {
1527 if (!(tp = new TABPTR)) {
1528 PGLOBAL g = t->G;
1529
1530 sprintf(g->Message, "NewPointer: %s", MSG(MEM_ALLOC_ERROR));
1531 throw 3;
1532 } else {
1533 tp->Next = t->P1;
1534 tp->Num = 0;
1535 t->P1 = tp;
1536 } /* endif tp */
1537 }
1538
1539 t->P1->Old[t->P1->Num] = oldv;
1540 t->P1->New[t->P1->Num++] = newv;
1541 } /* end of NewPointer */
1542
1543#if 0
1544/***********************************************************************/
1545/* Compare two files and return 0 if they are identical, else 1. */
1546/***********************************************************************/
1547int FileComp(PGLOBAL g, char *file1, char *file2)
1548 {
1549 char *fn[2], *bp[2], buff1[4096], buff2[4096];
1550 int i, k, n[2], h[2] = {-1,-1};
1551 int len[2], rc = -1;
1552
1553 fn[0] = file1; fn[1] = file2;
1554 bp[0] = buff1; bp[1] = buff2;
1555
1556 for (i = 0; i < 2; i++) {
1557#if defined(__WIN__)
1558 h[i]= global_open(g, MSGID_NONE, fn[i], _O_RDONLY | _O_BINARY);
1559#else // !__WIN__
1560 h[i]= global_open(g, MSGOD_NONE, fn[i], O_RDONLY);
1561#endif // !__WIN__
1562
1563 if (h[i] == -1) {
1564// if (errno != ENOENT) {
1565 sprintf(g->Message, MSG(OPEN_MODE_ERROR),
1566 "rb", (int)errno, fn[i]);
1567 strcat(strcat(g->Message, ": "), strerror(errno));
1568 throw 666;
1569 // } else
1570// len[i] = 0; // File does not exist yet
1571
1572 } else {
1573 if ((len[i] = _filelength(h[i])) < 0) {
1574 sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", fn[i]);
1575 throw 666;
1576 } // endif len
1577
1578 } // endif h
1579
1580 } // endfor i
1581
1582 if (len[0] != len[1])
1583 rc = 1;
1584
1585 while (rc == -1) {
1586 for (i = 0; i < 2; i++)
1587 if ((n[i] = read(h[i], bp[i], 4096)) < 0) {
1588 sprintf(g->Message, MSG(READ_ERROR), fn[i], strerror(errno));
1589 goto fin;
1590 } // endif n
1591
1592 if (n[0] != n[1])
1593 rc = 1;
1594 else if (*n == 0)
1595 rc = 0;
1596 else for (k = 0; k < *n; k++)
1597 if (*(bp[0] + k) != *(bp[1] + k)) {
1598 rc = 1;
1599 goto fin;
1600 } // endif bp
1601
1602 } // endwhile
1603
1604 fin:
1605 for (i = 0; i < 2; i++)
1606 if (h[i] != -1)
1607 close(h[i]);
1608
1609 return rc;
1610 } // end of FileComp
1611#endif // 0
1612